forked from xiongyuxing/tiku-backend.net
feat: add referral management endpoints
This commit is contained in:
@@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Tiku.Api.Contracts;
|
||||
using Tiku.Application.Auth;
|
||||
using Tiku.Application.Growth;
|
||||
using Tiku.Domain.Commerce;
|
||||
using Tiku.Domain.Growth;
|
||||
using Tiku.Domain.Identity;
|
||||
using Tiku.Domain.Tenancy;
|
||||
@@ -131,6 +132,77 @@ public sealed class ReferralEndpointTests
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Non_admin_cannot_access_referral_management()
|
||||
{
|
||||
await using var factory = new ApiTestFactory();
|
||||
var seed = await SeedReferralAsync(factory, includeCode: true);
|
||||
using var client = factory.CreateClient();
|
||||
await LoginAsync(client, seed.Student);
|
||||
|
||||
var response = await client.GetAsync("/api/referral/stats");
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Admin_can_manage_referral_team_and_query_stats()
|
||||
{
|
||||
await using var factory = new ApiTestFactory();
|
||||
var seed = await SeedReferralAsync(factory, includeCode: true);
|
||||
using var client = factory.CreateClient();
|
||||
await LoginAsync(client, seed.Admin);
|
||||
|
||||
var manualBind = await client.PostAsJsonAsync(
|
||||
"/api/referral/manual-bind",
|
||||
new ManualBindReferralDto
|
||||
{
|
||||
StudentUserId = seed.Student.UserId,
|
||||
ReferrerUserId = seed.Referrer.UserId,
|
||||
Source = "manual",
|
||||
Force = true
|
||||
});
|
||||
var team = await client.PutAsJsonAsync(
|
||||
"/api/referral/team",
|
||||
new UpsertReferralTeamDto
|
||||
{
|
||||
MemberUserId = seed.Referrer.UserId,
|
||||
LeaderUserId = seed.Admin.UserId,
|
||||
RelationType = "sales_team",
|
||||
Status = "active"
|
||||
});
|
||||
await factory.SeedAsync(new Order
|
||||
{
|
||||
TenantId = seed.TenantId,
|
||||
UserId = seed.Student.UserId,
|
||||
OrderNo = "R202607260001",
|
||||
Status = OrderStatus.Paid,
|
||||
ProductType = "svip",
|
||||
ProductName = "SVIP",
|
||||
AmountCents = 9900,
|
||||
PaidAt = DateTimeOffset.UtcNow
|
||||
});
|
||||
|
||||
var stats = await client.GetAsync($"/api/referral/stats?referrerUserId={seed.Referrer.UserId}");
|
||||
var salesStats = await client.GetAsync("/api/referral/sales-stats");
|
||||
var conversion = await client.GetAsync("/api/referral/conversion-report?days=30");
|
||||
var clients = await client.GetAsync($"/api/referral/sales-clients?referrerUserId={seed.Referrer.UserId}");
|
||||
var teamList = await client.GetAsync($"/api/referral/team?leaderUserId={seed.Admin.UserId}");
|
||||
var statsItem = await stats.Content.ReadFromJsonAsync<ReferralStatsItem>();
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, manualBind.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, team.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, stats.StatusCode);
|
||||
Assert.Equal(1, statsItem!.LeadCount);
|
||||
Assert.Equal(1, statsItem.PaidLeadCount);
|
||||
Assert.Equal(9900, statsItem.PaidAmountCents);
|
||||
Assert.Equal(HttpStatusCode.OK, salesStats.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, conversion.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, clients.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, teamList.StatusCode);
|
||||
Assert.Contains(seed.Referrer.UserId.ToString(), await teamList.Content.ReadAsStringAsync(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static async Task<ReferralSeed> SeedReferralAsync(
|
||||
ApiTestFactory factory,
|
||||
bool includeCode = false)
|
||||
@@ -139,14 +211,17 @@ public sealed class ReferralEndpointTests
|
||||
var tenantCode = tenantId.ToString("N");
|
||||
var referrer = new LoginSeed(tenantId, Guid.NewGuid(), "13800001001");
|
||||
var student = new LoginSeed(tenantId, Guid.NewGuid(), "13800001002");
|
||||
var admin = new LoginSeed(tenantId, Guid.NewGuid(), "13800001003");
|
||||
const string referralCode = "ABC12345";
|
||||
var entities = new List<object>
|
||||
{
|
||||
new Tenant { Id = tenantId, Slug = tenantCode, Name = "Referral Tenant" },
|
||||
User(referrer.UserId, referrer.Phone, "Referral Teacher", "teacher"),
|
||||
User(student.UserId, student.Phone, "Referral Student", "student"),
|
||||
User(admin.UserId, admin.Phone, "Referral Admin", "tenant_admin"),
|
||||
Identity(referrer),
|
||||
Identity(student),
|
||||
Identity(admin),
|
||||
new TenantMembership
|
||||
{
|
||||
TenantId = tenantId,
|
||||
@@ -160,6 +235,13 @@ public sealed class ReferralEndpointTests
|
||||
UserId = student.UserId,
|
||||
Role = TenantRole.Student,
|
||||
Status = MembershipStatus.Active
|
||||
},
|
||||
new TenantMembership
|
||||
{
|
||||
TenantId = tenantId,
|
||||
UserId = admin.UserId,
|
||||
Role = TenantRole.TenantAdmin,
|
||||
Status = MembershipStatus.Active
|
||||
}
|
||||
};
|
||||
if (includeCode)
|
||||
@@ -174,7 +256,7 @@ public sealed class ReferralEndpointTests
|
||||
}
|
||||
|
||||
await factory.SeedAsync(entities.ToArray());
|
||||
return new ReferralSeed(tenantId, tenantCode, referrer, student, referralCode);
|
||||
return new ReferralSeed(tenantId, tenantCode, referrer, student, admin, referralCode);
|
||||
}
|
||||
|
||||
private static User User(Guid userId, string phone, string name, string role)
|
||||
@@ -233,5 +315,6 @@ public sealed class ReferralEndpointTests
|
||||
string TenantCode,
|
||||
LoginSeed Referrer,
|
||||
LoginSeed Student,
|
||||
LoginSeed Admin,
|
||||
string ReferralCode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user