Make source-link listings reachable + skip uncontactable applicants
(1) The contact modal only offered a click-through link for Divar sources, so medboom/ iranestekhdam/channel listings with no inline phone looked uncontactable. Offer the source link for ANY source («مشاهده آگهی در منبع»), for talent, shifts, and jobs alike — rescuing the dead applicant cards that actually have a source URL. (2) At publish, skip an applicant («آماده به کار») that has NO contact path at all — no phone, no contact channel, and no source URL. Such a card cannot reach anyone. Existing ones drop out when the talent reprocess button rebuilds the board. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -426,8 +426,8 @@ app.MapGet("/contact", async (string? type, int id, AppDbContext db, InterestSer
|
|||||||
if (!string.IsNullOrWhiteSpace(s.Facility!.BaleId)) items.Add(Item(ContactType.Bale, s.Facility.BaleId!));
|
if (!string.IsNullOrWhiteSpace(s.Facility!.BaleId)) items.Add(Item(ContactType.Bale, s.Facility.BaleId!));
|
||||||
}
|
}
|
||||||
if (items.Count == 0 && !string.IsNullOrWhiteSpace(s.SourceUrl)
|
if (items.Count == 0 && !string.IsNullOrWhiteSpace(s.SourceUrl)
|
||||||
&& Uri.TryCreate(s.SourceUrl, UriKind.Absolute, out var ss) && ss.Host.Contains("divar"))
|
&& Uri.TryCreate(s.SourceUrl, UriKind.Absolute, out var ss))
|
||||||
{ fallbackUrl = s.SourceUrl; fallbackLabel = "مشاهده شماره در دیوار ↗"; }
|
{ fallbackUrl = s.SourceUrl; fallbackLabel = ss.Host.Contains("divar") ? "مشاهده شماره در دیوار ↗" : "مشاهده آگهی در منبع ↗"; }
|
||||||
await interest.LogAsync(InterestEventType.Apply, id);
|
await interest.LogAsync(InterestEventType.Apply, id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -444,8 +444,8 @@ app.MapGet("/contact", async (string? type, int id, AppDbContext db, InterestSer
|
|||||||
if (!string.IsNullOrWhiteSpace(j.Facility!.BaleId)) items.Add(Item(ContactType.Bale, j.Facility.BaleId!));
|
if (!string.IsNullOrWhiteSpace(j.Facility!.BaleId)) items.Add(Item(ContactType.Bale, j.Facility.BaleId!));
|
||||||
}
|
}
|
||||||
if (items.Count == 0 && !string.IsNullOrWhiteSpace(j.SourceUrl)
|
if (items.Count == 0 && !string.IsNullOrWhiteSpace(j.SourceUrl)
|
||||||
&& Uri.TryCreate(j.SourceUrl, UriKind.Absolute, out var js) && js.Host.Contains("divar"))
|
&& Uri.TryCreate(j.SourceUrl, UriKind.Absolute, out var js))
|
||||||
{ fallbackUrl = j.SourceUrl; fallbackLabel = "مشاهده شماره در دیوار ↗"; }
|
{ fallbackUrl = j.SourceUrl; fallbackLabel = js.Host.Contains("divar") ? "مشاهده شماره در دیوار ↗" : "مشاهده آگهی در منبع ↗"; }
|
||||||
await interest.LogJobAsync(InterestEventType.Apply, id);
|
await interest.LogJobAsync(InterestEventType.Apply, id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -458,8 +458,8 @@ app.MapGet("/contact", async (string? type, int id, AppDbContext db, InterestSer
|
|||||||
items.AddRange(t.Contacts.OrderBy(c => c.SortOrder).Select(c => Item(c.Type, c.Value)));
|
items.AddRange(t.Contacts.OrderBy(c => c.SortOrder).Select(c => Item(c.Type, c.Value)));
|
||||||
if (items.Count == 0 && !string.IsNullOrWhiteSpace(t.Phone)) items.Add(Item(ContactType.Mobile, t.Phone!));
|
if (items.Count == 0 && !string.IsNullOrWhiteSpace(t.Phone)) items.Add(Item(ContactType.Mobile, t.Phone!));
|
||||||
if (items.Count == 0 && !string.IsNullOrWhiteSpace(t.SourceUrl)
|
if (items.Count == 0 && !string.IsNullOrWhiteSpace(t.SourceUrl)
|
||||||
&& Uri.TryCreate(t.SourceUrl, UriKind.Absolute, out var su) && su.Host.Contains("divar"))
|
&& Uri.TryCreate(t.SourceUrl, UriKind.Absolute, out var su))
|
||||||
{ fallbackUrl = t.SourceUrl; fallbackLabel = "مشاهده شماره در دیوار ↗"; }
|
{ fallbackUrl = t.SourceUrl; fallbackLabel = su.Host.Contains("divar") ? "مشاهده شماره در دیوار ↗" : "مشاهده آگهی در منبع ↗"; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: return Results.BadRequest();
|
default: return Results.BadRequest();
|
||||||
|
|||||||
@@ -677,6 +677,13 @@ public class IngestionService
|
|||||||
// «آماده به کار» — a worker offering themselves. No facility involved.
|
// «آماده به کار» — a worker offering themselves. No facility involved.
|
||||||
if (parsed.Kind == ListingKind.Talent || kindStr.Contains("talent") || kindStr.Contains("آماده"))
|
if (parsed.Kind == ListingKind.Talent || kindStr.Contains("talent") || kindStr.Contains("آماده"))
|
||||||
{
|
{
|
||||||
|
// Skip uncontactable applicants: no phone, no other contact channel, AND no source link to
|
||||||
|
// click through to — such a card is useless to an employer. (A source URL is enough, since
|
||||||
|
// the detail page now offers a «مشاهده آگهی در منبع» link for any source.)
|
||||||
|
var reachable = !string.IsNullOrWhiteSpace(d?.Phone) || !string.IsNullOrWhiteSpace(parsed.Phone)
|
||||||
|
|| parsed.Contacts.Count > 0 || !string.IsNullOrWhiteSpace(raw.SourceUrl);
|
||||||
|
if (!reachable) { raw.Status = RawListingStatus.Discarded; return; }
|
||||||
|
|
||||||
// ONE person = ONE listing. Do NOT fan out across roles: an applicant has a single
|
// ONE person = ONE listing. Do NOT fan out across roles: an applicant has a single
|
||||||
// profession, and «پرستار» + «پرستار کودک» from the same ad were producing duplicate
|
// profession, and «پرستار» + «پرستار کودک» from the same ad were producing duplicate
|
||||||
// cards. Use the primary (AI) role; any secondary role names become searchable tags.
|
// cards. Use the primary (AI) role; any secondary role names become searchable tags.
|
||||||
|
|||||||
Reference in New Issue
Block a user