feat: Initialize MissionService database schema, add MiningService unit tests, and update deployment configurations.
This commit is contained in:
@@ -478,6 +478,57 @@ services:
|
||||
- "traefik.http.services.mining-service.loadbalancer.sticky.cookie=true"
|
||||
- "traefik.http.services.mining-service.loadbalancer.sticky.cookie.name=mining_session"
|
||||
|
||||
# Mission Service .NET - Gamification (Check-ins, Missions, Tasks)
|
||||
mission-service-net:
|
||||
build:
|
||||
context: ../../services/mission-service-net
|
||||
dockerfile: Dockerfile
|
||||
image: goodgo/mission-service-net:latest
|
||||
container_name: mission-service-net-local
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://+:8080
|
||||
# EN: Database - Neon PostgreSQL
|
||||
# VI: Cơ sở dữ liệu - Neon PostgreSQL
|
||||
- ConnectionStrings__DefaultConnection=Host=ep-holy-glitter-a4hongg7-pooler.us-east-1.aws.neon.tech;Port=5432;Database=mission_service;Username=neondb_owner;Password=npg_Ssfy6HKO0cXI;SSL Mode=Require
|
||||
# EN: IAM Service Communication
|
||||
# VI: Giao tiếp IAM Service
|
||||
- IamService__BaseUrl=http://iam-service-net:8080
|
||||
- IamService__ServiceName=mission-service
|
||||
# EN: JWT Configuration
|
||||
# VI: Cấu hình JWT
|
||||
- Jwt__Authority=http://iam-service-net:8080
|
||||
- Jwt__Audience=goodgo-api
|
||||
- Jwt__RequireHttpsMetadata=false
|
||||
# EN: Redis Cache
|
||||
# VI: Cache Redis
|
||||
- Redis__Host=167.114.174.113
|
||||
- Redis__Port=6379
|
||||
- Redis__Password=Velik@2026
|
||||
ports:
|
||||
- "5007:8080"
|
||||
depends_on:
|
||||
iam-service-net:
|
||||
condition: service_healthy
|
||||
traefik:
|
||||
condition: service_started
|
||||
networks:
|
||||
- microservices-network
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health/live"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.mission-service.rule=PathPrefix(`/api/v1/checkins`) || PathPrefix(`/api/v1/missions`) || PathPrefix(`/api/v1/admin/missions`) || PathPrefix(`/api/v1/admin/checkins`) || PathPrefix(`/api/v1/admin/tasks`)"
|
||||
- "traefik.http.routers.mission-service.entrypoints=web"
|
||||
- "traefik.http.services.mission-service.loadbalancer.server.port=8080"
|
||||
- "traefik.http.services.mission-service.loadbalancer.healthcheck.path=/health/live"
|
||||
- "traefik.http.services.mission-service.loadbalancer.healthcheck.interval=10s"
|
||||
|
||||
|
||||
# Jaeger - Distributed Tracing
|
||||
# jaeger:
|
||||
|
||||
@@ -401,9 +401,11 @@ sequenceDiagram
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/v1/admin/analytics/overview` | Dashboard overview stats |
|
||||
| `GET` | `/api/v1/admin/analytics/mining` | Mining statistics |
|
||||
| `GET` | `/api/v1/admin/analytics/streaks` | Streak distribution |
|
||||
| `GET` | `/api/v1/admin/analytics/miners` | Miner statistics |
|
||||
| `GET` | `/api/v1/admin/analytics/circles` | Circle statistics |
|
||||
| `GET` | `/api/v1/admin/analytics/referrals` | Referral network stats |
|
||||
| `GET` | `/api/v1/admin/analytics/points` | Points statistics |
|
||||
| `GET` | `/api/v1/admin/analytics/streaks` | Streak distribution |
|
||||
| `GET` | `/api/v1/admin/audit-logs` | View configuration change logs |
|
||||
|
||||
---
|
||||
|
||||
@@ -401,9 +401,11 @@ sequenceDiagram
|
||||
| Phương Thức | Endpoint | Mô Tả |
|
||||
|-------------|----------|-------|
|
||||
| `GET` | `/api/v1/admin/analytics/overview` | Thống kê tổng quan dashboard |
|
||||
| `GET` | `/api/v1/admin/analytics/mining` | Thống kê đào |
|
||||
| `GET` | `/api/v1/admin/analytics/streaks` | Phân bố streak |
|
||||
| `GET` | `/api/v1/admin/analytics/miners` | Thống kê thợ đào |
|
||||
| `GET` | `/api/v1/admin/analytics/circles` | Thống kê vòng tròn an toàn |
|
||||
| `GET` | `/api/v1/admin/analytics/referrals` | Thống kê mạng lưới giới thiệu |
|
||||
| `GET` | `/api/v1/admin/analytics/points` | Thống kê điểm số |
|
||||
| `GET` | `/api/v1/admin/analytics/streaks` | Phân bố streak |
|
||||
| `GET` | `/api/v1/admin/audit-logs` | Xem nhật ký thay đổi cấu hình |
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
using FluentAssertions;
|
||||
using MiningService.API.Application.Commands;
|
||||
using MiningService.Domain.AggregatesModel.MinerAggregate;
|
||||
using MiningService.Domain.Exceptions;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace MiningService.UnitTests.Application.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Unit tests for Admin Command Handlers.
|
||||
/// VI: Unit tests cho Admin Command Handlers.
|
||||
/// </summary>
|
||||
public class AdminCommandHandlerTests
|
||||
{
|
||||
private readonly IMinerRepository _minerRepository;
|
||||
|
||||
public AdminCommandHandlerTests()
|
||||
{
|
||||
_minerRepository = Substitute.For<IMinerRepository>();
|
||||
}
|
||||
|
||||
#region BanMinerCommandHandler Tests
|
||||
|
||||
[Fact]
|
||||
public async Task BanMiner_ExistingMiner_BansSuccessfully()
|
||||
{
|
||||
// Arrange
|
||||
var minerId = Guid.NewGuid();
|
||||
var miner = Miner.Create(Guid.NewGuid());
|
||||
var command = new BanMinerCommand(minerId, "Violation");
|
||||
var handler = new BanMinerCommandHandler(_minerRepository);
|
||||
|
||||
_minerRepository.GetByIdAsync(minerId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
_minerRepository.UnitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.Success.Should().BeTrue();
|
||||
miner.Status.Should().Be(MinerStatus.Suspended);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BanMiner_MinerNotFound_ThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var minerId = Guid.NewGuid();
|
||||
var command = new BanMinerCommand(minerId, "Violation");
|
||||
var handler = new BanMinerCommandHandler(_minerRepository);
|
||||
|
||||
_minerRepository.GetByIdAsync(minerId, Arg.Any<CancellationToken>())
|
||||
.Returns((Miner?)null);
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<MinerNotFoundException>(() =>
|
||||
handler.Handle(command, CancellationToken.None));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AdjustMinerPointsCommandHandler Tests
|
||||
|
||||
[Fact]
|
||||
public async Task AdjustPoints_PositiveAmount_AddsPoints()
|
||||
{
|
||||
// Arrange
|
||||
var minerId = Guid.NewGuid();
|
||||
var miner = Miner.Create(Guid.NewGuid());
|
||||
var command = new AdjustMinerPointsCommand(minerId, 100m, "Admin bonus");
|
||||
var handler = new AdjustMinerPointsCommandHandler(_minerRepository);
|
||||
|
||||
_minerRepository.GetByIdAsync(minerId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
_minerRepository.UnitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.Success.Should().BeTrue();
|
||||
result.NewBalance.Should().Be(100m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdjustPoints_MinerNotFound_ThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var minerId = Guid.NewGuid();
|
||||
var command = new AdjustMinerPointsCommand(minerId, 100m, "Bonus");
|
||||
var handler = new AdjustMinerPointsCommandHandler(_minerRepository);
|
||||
|
||||
_minerRepository.GetByIdAsync(minerId, Arg.Any<CancellationToken>())
|
||||
.Returns((Miner?)null);
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<MinerNotFoundException>(() =>
|
||||
handler.Handle(command, CancellationToken.None));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ResetMinerStreakCommandHandler Tests
|
||||
|
||||
[Fact]
|
||||
public async Task ResetStreak_ValidMiner_ResetsSuccessfully()
|
||||
{
|
||||
// Arrange
|
||||
var minerId = Guid.NewGuid();
|
||||
var miner = Miner.Create(Guid.NewGuid());
|
||||
var command = new ResetMinerStreakCommand(minerId, "Admin reset");
|
||||
var handler = new ResetMinerStreakCommandHandler(_minerRepository);
|
||||
|
||||
_minerRepository.GetByIdAsync(minerId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
_minerRepository.UnitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.Success.Should().BeTrue();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using FluentAssertions;
|
||||
using MiningService.API.Application.Commands;
|
||||
using MiningService.Domain.AggregatesModel.CircleAggregate;
|
||||
using MiningService.Domain.AggregatesModel.MinerAggregate;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace MiningService.UnitTests.Application.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Unit tests for Circle Command Handlers.
|
||||
/// VI: Unit tests cho Circle Command Handlers.
|
||||
/// </summary>
|
||||
public class CircleCommandHandlerTests
|
||||
{
|
||||
private readonly ICircleRepository _circleRepository;
|
||||
private readonly IMinerRepository _minerRepository;
|
||||
|
||||
public CircleCommandHandlerTests()
|
||||
{
|
||||
_circleRepository = Substitute.For<ICircleRepository>();
|
||||
_minerRepository = Substitute.For<IMinerRepository>();
|
||||
}
|
||||
|
||||
#region CreateCircleCommandHandler Tests
|
||||
|
||||
[Fact]
|
||||
public async Task CreateCircle_ValidInput_CreatesNewCircle()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId);
|
||||
var command = new CreateCircleCommand(userId, "Test Circle");
|
||||
var handler = new CreateCircleCommandHandler(_circleRepository, _minerRepository);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
_circleRepository.UnitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.CircleId.Should().NotBeEmpty();
|
||||
|
||||
_circleRepository.Received(1).Add(Arg.Is<Circle>(c => c.Name == "Test Circle"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateCircle_MinerAlreadyInCircle_ThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId);
|
||||
miner.JoinCircle(Guid.NewGuid()); // Already in a circle
|
||||
var command = new CreateCircleCommand(userId, "New Circle");
|
||||
var handler = new CreateCircleCommandHandler(_circleRepository, _minerRepository);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
|
||||
// Act & Assert
|
||||
var act = () => handler.Handle(command, CancellationToken.None);
|
||||
await act.Should().ThrowAsync<Exception>().WithMessage("*already*");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region InviteToCircleCommandHandler Tests
|
||||
|
||||
[Fact]
|
||||
public async Task InviteToCircle_ValidInput_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var ownerId = Guid.NewGuid();
|
||||
var inviteeId = Guid.NewGuid();
|
||||
var circle = Circle.Create(ownerId, "Test Circle");
|
||||
var inviter = Miner.Create(ownerId);
|
||||
var invitee = Miner.Create(inviteeId);
|
||||
var command = new InviteToCircleCommand(ownerId, inviteeId);
|
||||
var handler = new InviteToCircleCommandHandler(_circleRepository, _minerRepository);
|
||||
|
||||
_circleRepository.GetByOwnerIdAsync(ownerId, Arg.Any<CancellationToken>())
|
||||
.Returns(circle);
|
||||
_minerRepository.GetByUserIdAsync(ownerId, Arg.Any<CancellationToken>())
|
||||
.Returns(inviter);
|
||||
_minerRepository.GetByUserIdAsync(inviteeId, Arg.Any<CancellationToken>())
|
||||
.Returns(invitee);
|
||||
_circleRepository.UnitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using FluentAssertions;
|
||||
using MiningService.API.Application.Commands;
|
||||
using MiningService.Domain.AggregatesModel.MinerAggregate;
|
||||
using MiningService.Domain.Exceptions;
|
||||
using MiningService.Domain.SeedWork;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace MiningService.UnitTests.Application.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Unit tests for ClaimMiningRewardCommandHandler.
|
||||
/// VI: Unit tests cho ClaimMiningRewardCommandHandler.
|
||||
/// </summary>
|
||||
public class ClaimMiningRewardCommandHandlerTests
|
||||
{
|
||||
private readonly IMinerRepository _minerRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ClaimMiningRewardCommandHandler _handler;
|
||||
|
||||
public ClaimMiningRewardCommandHandlerTests()
|
||||
{
|
||||
_unitOfWork = Substitute.For<IUnitOfWork>();
|
||||
_minerRepository = Substitute.For<IMinerRepository>();
|
||||
_minerRepository.UnitOfWork.Returns(_unitOfWork);
|
||||
_handler = new ClaimMiningRewardCommandHandler(_minerRepository);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_ReadySession_ReturnsReward()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId);
|
||||
miner.StartMiningSession(configBaseRate: 0.25m, sessionHours: 0); // Immediate claim
|
||||
var command = new ClaimMiningRewardCommand(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
_minerRepository.UnitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await _handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.PointsEarned.Should().BeGreaterThanOrEqualTo(0);
|
||||
result.TotalPoints.Should().BeGreaterThanOrEqualTo(0);
|
||||
|
||||
await _minerRepository.Received(1).UnitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_MinerNotFound_ThrowsNotFoundException()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var command = new ClaimMiningRewardCommand(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns((Miner?)null);
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<MinerNotFoundException>(() =>
|
||||
_handler.Handle(command, CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_NoActiveSession_ThrowsDomainException()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId); // No session started
|
||||
var command = new ClaimMiningRewardCommand(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<MiningDomainException>(() =>
|
||||
_handler.Handle(command, CancellationToken.None));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using FluentAssertions;
|
||||
using MiningService.API.Application.Commands;
|
||||
using MiningService.Domain.AggregatesModel.MinerAggregate;
|
||||
using MiningService.Domain.Exceptions;
|
||||
using MiningService.Domain.SeedWork;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace MiningService.UnitTests.Application.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Unit tests for StartMiningCommandHandler.
|
||||
/// VI: Unit tests cho StartMiningCommandHandler.
|
||||
/// </summary>
|
||||
public class StartMiningCommandHandlerTests
|
||||
{
|
||||
private readonly IMinerRepository _minerRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly StartMiningCommandHandler _handler;
|
||||
|
||||
public StartMiningCommandHandlerTests()
|
||||
{
|
||||
_unitOfWork = Substitute.For<IUnitOfWork>();
|
||||
_unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);
|
||||
|
||||
_minerRepository = Substitute.For<IMinerRepository>();
|
||||
_minerRepository.UnitOfWork.Returns(_unitOfWork);
|
||||
_handler = new StartMiningCommandHandler(_minerRepository);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_ExistingMiner_StartsSession()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId);
|
||||
var command = new StartMiningCommand(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
|
||||
// Act
|
||||
var result = await _handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.SessionId.Should().NotBeEmpty();
|
||||
result.HourlyRate.Should().BeGreaterThan(0);
|
||||
|
||||
await _unitOfWork.Received(1).SaveEntitiesAsync(Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_NewUser_CreatesMinerAndStartsSession()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var command = new StartMiningCommand(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns((Miner?)null);
|
||||
|
||||
// Act
|
||||
var result = await _handler.Handle(command, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.SessionId.Should().NotBeEmpty();
|
||||
|
||||
_minerRepository.Received(1).Add(Arg.Is<Miner>(m => m.UserId == userId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_SuspendedMiner_ThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId);
|
||||
miner.Suspend();
|
||||
var command = new StartMiningCommand(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<MiningDomainException>(() =>
|
||||
_handler.Handle(command, CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_AlreadyMining_ThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId);
|
||||
miner.StartMiningSession();
|
||||
var command = new StartMiningCommand(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<MiningDomainException>(() =>
|
||||
_handler.Handle(command, CancellationToken.None));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
using FluentAssertions;
|
||||
using MiningService.API.Application.Queries;
|
||||
using MiningService.Domain.AggregatesModel.MinerAggregate;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace MiningService.UnitTests.Application.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Unit tests for GetMinerStatusQueryHandler.
|
||||
/// VI: Unit tests cho GetMinerStatusQueryHandler.
|
||||
/// </summary>
|
||||
public class GetMinerStatusQueryHandlerTests
|
||||
{
|
||||
private readonly IMinerRepository _minerRepository;
|
||||
private readonly GetMinerStatusQueryHandler _handler;
|
||||
|
||||
public GetMinerStatusQueryHandlerTests()
|
||||
{
|
||||
_minerRepository = Substitute.For<IMinerRepository>();
|
||||
_handler = new GetMinerStatusQueryHandler(_minerRepository);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_ExistingMiner_ReturnsMinerStatus()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId);
|
||||
var query = new GetMinerStatusQuery(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
|
||||
// Act
|
||||
var result = await _handler.Handle(query, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.MinerId.Should().Be(miner.Id);
|
||||
result.Role.Should().Be("Pioneer");
|
||||
result.TotalMinedPoints.Should().Be(0);
|
||||
result.HourlyRate.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_MinerNotFound_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var query = new GetMinerStatusQuery(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns((Miner?)null);
|
||||
|
||||
// Act
|
||||
var result = await _handler.Handle(query, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_MinerWithActiveSession_ReturnsSessionInfo()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var miner = Miner.Create(userId);
|
||||
miner.StartMiningSession();
|
||||
var query = new GetMinerStatusQuery(userId);
|
||||
|
||||
_minerRepository.GetByUserIdAsync(userId, Arg.Any<CancellationToken>())
|
||||
.Returns(miner);
|
||||
|
||||
// Act
|
||||
var result = await _handler.Handle(query, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.HasActiveSession.Should().BeTrue();
|
||||
result.SessionEndTime.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
<ItemGroup>
|
||||
<!-- EN: Test framework / VI: Test framework -->
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -17,7 +18,7 @@
|
||||
</PackageReference>
|
||||
|
||||
<!-- EN: Assertions and mocking / VI: Assertions và mocking -->
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.2" />
|
||||
<PackageReference Include="FluentAssertions" Version="8.8.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
|
||||
<!-- EN: Coverage / VI: Coverage -->
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
# EN: Docker Compose for local development
|
||||
# VI: Docker Compose cho phát triển local
|
||||
|
||||
services:
|
||||
mission-api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: mission-api
|
||||
ports:
|
||||
- "5000:8080"
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- DATABASE_URL=Host=postgres;Port=5432;Database=mission_db;Username=postgres;Password=postgres
|
||||
- REDIS_URL=redis:6379
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- mission-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health/live"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: mission-postgres
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: mission_db
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- mission-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: mission-redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- mission-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
|
||||
networks:
|
||||
mission-network:
|
||||
driver: bridge
|
||||
@@ -14,6 +14,10 @@
|
||||
<!-- EN: FluentValidation for request validation / VI: FluentValidation cho validation request -->
|
||||
<PackageReference Include="FluentValidation" Version="11.11.0" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!-- EN: Swagger/OpenAPI / VI: Swagger/OpenAPI -->
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
@@ -33,6 +37,9 @@
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0" />
|
||||
|
||||
<!-- EN: JWT Authentication / VI: JWT Authentication -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -96,6 +96,22 @@ try
|
||||
});
|
||||
});
|
||||
|
||||
// EN: Add Authentication and Authorization / VI: Thêm Authentication và Authorization
|
||||
builder.Services.AddAuthentication("Bearer")
|
||||
.AddJwtBearer("Bearer", options =>
|
||||
{
|
||||
options.Authority = builder.Configuration["Jwt:Authority"] ?? "http://iam-service-net:8080";
|
||||
options.Audience = builder.Configuration["Jwt:Audience"] ?? "goodgo-api";
|
||||
options.RequireHttpsMetadata = false; // EN: Development only / VI: Chỉ development
|
||||
options.TokenValidationParameters = new()
|
||||
{
|
||||
ValidateIssuer = false, // EN: IAM service validates / VI: IAM service xác thực
|
||||
ValidateAudience = false,
|
||||
ValidateLifetime = true
|
||||
};
|
||||
});
|
||||
builder.Services.AddAuthorization();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// EN: Configure middleware pipeline / VI: Cấu hình middleware pipeline
|
||||
@@ -114,6 +130,10 @@ try
|
||||
|
||||
app.UseCors();
|
||||
app.UseRouting();
|
||||
|
||||
// EN: Add Authentication and Authorization / VI: Thêm Authentication và Authorization
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
// EN: Map health check endpoints / VI: Map health check endpoints
|
||||
app.MapHealthChecks("/health");
|
||||
|
||||
@@ -0,0 +1,740 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using MissionService.Infrastructure;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MissionService.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(MissionDbContext))]
|
||||
[Migration("20260117134348_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.CheckInAggregate.CheckInDay", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date");
|
||||
|
||||
b.Property<bool>("IsMilestone")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_milestone");
|
||||
|
||||
b.Property<decimal>("PointsEarned")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasColumnName("points_earned");
|
||||
|
||||
b.Property<int>("StreakOnDay")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("streak_on_day");
|
||||
|
||||
b.Property<Guid>("user_checkin_id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("user_checkin_id", "Date")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("checkin_days", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.CheckInAggregate.UserCheckIn", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<int>("CurrentStreak")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("current_streak");
|
||||
|
||||
b.Property<DateOnly?>("LastCheckInDate")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("last_checkin_date");
|
||||
|
||||
b.Property<int>("LongestStreak")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("longest_streak");
|
||||
|
||||
b.Property<int>("TotalCheckIns")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("total_checkins");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("user_checkins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.FrequencyType", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("frequency_types", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Once"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Daily"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Weekly"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Unlimited"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.Mission", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<int>("Category")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("category_id");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("code");
|
||||
|
||||
b.Property<string>("DescriptionEn")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasColumnName("description_en");
|
||||
|
||||
b.Property<string>("DescriptionVi")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasColumnName("description_vi");
|
||||
|
||||
b.Property<DateTime?>("EndDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("end_date");
|
||||
|
||||
b.Property<int>("Frequency")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("frequency_id");
|
||||
|
||||
b.Property<int>("MaxCompletions")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("max_completions");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("priority");
|
||||
|
||||
b.Property<DateTime>("StartDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("start_date");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status_id");
|
||||
|
||||
b.Property<string>("TitleEn")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("title_en");
|
||||
|
||||
b.Property<string>("TitleVi")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("title_vi");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("type_id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("missions", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("mission_categories", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Daily"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Weekly"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Special"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Onboarding"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Event"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionRule", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Metadata")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("metadata");
|
||||
|
||||
b.Property<string>("Operator")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("operator");
|
||||
|
||||
b.Property<string>("RuleType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("rule_type");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("value");
|
||||
|
||||
b.Property<Guid>("mission_id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("mission_id");
|
||||
|
||||
b.ToTable("mission_rules", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionStatus", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("mission_statuses", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Draft"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Active"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Paused"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Expired"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Archived"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionType", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("mission_types", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Video"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Click"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Upload"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Invite"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Name = "CheckIn"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 6,
|
||||
Name = "Social"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.RewardAggregate.UserReward", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime?>("ClaimedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("claimed_at");
|
||||
|
||||
b.Property<DateTime>("EarnedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("earned_at");
|
||||
|
||||
b.Property<DateTime?>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expires_at");
|
||||
|
||||
b.Property<Guid>("SourceId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("source_id");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("type");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SourceId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Status", "ExpiresAt");
|
||||
|
||||
b.ToTable("user_rewards", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.TaskAggregate.TaskStatus", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("task_statuses", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Pending"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "InProgress"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "PendingVerification"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Completed"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Rejected"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 6,
|
||||
Name = "Cancelled"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 7,
|
||||
Name = "Expired"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.TaskAggregate.UserTask", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime?>("ClaimedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("claimed_at");
|
||||
|
||||
b.Property<DateTime?>("CompletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("completed_at");
|
||||
|
||||
b.Property<Guid>("MissionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("mission_id");
|
||||
|
||||
b.Property<bool>("RewardClaimed")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("reward_claimed");
|
||||
|
||||
b.Property<DateTime>("StartedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("started_at");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status_id");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MissionId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("UserId", "MissionId");
|
||||
|
||||
b.ToTable("user_tasks", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.CheckInAggregate.CheckInDay", b =>
|
||||
{
|
||||
b.HasOne("MissionService.Domain.AggregatesModel.CheckInAggregate.UserCheckIn", null)
|
||||
.WithMany("CheckInDays")
|
||||
.HasForeignKey("user_checkin_id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.Mission", b =>
|
||||
{
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.MissionAggregate.MissionReward", "Reward", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("MissionId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<string>("BadgeId")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("reward_badge_id");
|
||||
|
||||
b1.Property<int>("ExperiencePoints")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("reward_xp");
|
||||
|
||||
b1.Property<decimal>("MiningBoostPercent")
|
||||
.HasPrecision(5, 2)
|
||||
.HasColumnType("numeric(5,2)")
|
||||
.HasColumnName("reward_mining_boost");
|
||||
|
||||
b1.Property<decimal>("Points")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasColumnName("reward_points");
|
||||
|
||||
b1.HasKey("MissionId");
|
||||
|
||||
b1.ToTable("missions");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("MissionId");
|
||||
});
|
||||
|
||||
b.Navigation("Reward")
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionRule", b =>
|
||||
{
|
||||
b.HasOne("MissionService.Domain.AggregatesModel.MissionAggregate.Mission", null)
|
||||
.WithMany("Rules")
|
||||
.HasForeignKey("mission_id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.RewardAggregate.UserReward", b =>
|
||||
{
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.RewardAggregate.RewardAmount", "Amount", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("UserRewardId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<decimal>("BonusPoints")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasColumnName("bonus_points");
|
||||
|
||||
b1.Property<string>("Currency")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasDefaultValue("MP")
|
||||
.HasColumnName("currency");
|
||||
|
||||
b1.Property<decimal>("Points")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasColumnName("points");
|
||||
|
||||
b1.HasKey("UserRewardId");
|
||||
|
||||
b1.ToTable("user_rewards");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("UserRewardId");
|
||||
});
|
||||
|
||||
b.Navigation("Amount")
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.TaskAggregate.UserTask", b =>
|
||||
{
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.TaskAggregate.TaskEvidence", "Evidence", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("UserTaskId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<DateTime>("CapturedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("evidence_captured_at");
|
||||
|
||||
b1.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)")
|
||||
.HasColumnName("evidence_data");
|
||||
|
||||
b1.Property<string>("ScreenshotUrl")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("evidence_screenshot_url");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("evidence_type");
|
||||
|
||||
b1.Property<string>("VideoUrl")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("evidence_video_url");
|
||||
|
||||
b1.HasKey("UserTaskId");
|
||||
|
||||
b1.ToTable("user_tasks");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("UserTaskId");
|
||||
});
|
||||
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.TaskAggregate.TaskProgress", "Progress", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("UserTaskId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<int>("CurrentValue")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("progress_current");
|
||||
|
||||
b1.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("progress_updated_at");
|
||||
|
||||
b1.Property<int>("TargetValue")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("progress_target");
|
||||
|
||||
b1.HasKey("UserTaskId");
|
||||
|
||||
b1.ToTable("user_tasks");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("UserTaskId");
|
||||
});
|
||||
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.TaskAggregate.VerificationResult", "Verification", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("UserTaskId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<string>("FailureReason")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("verification_failure_reason");
|
||||
|
||||
b1.Property<bool>("IsValid")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("verification_valid");
|
||||
|
||||
b1.Property<int>("Method")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("verification_method");
|
||||
|
||||
b1.Property<DateTime>("VerifiedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("verification_at");
|
||||
|
||||
b1.Property<Guid?>("VerifiedBy")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("verification_by");
|
||||
|
||||
b1.HasKey("UserTaskId");
|
||||
|
||||
b1.ToTable("user_tasks");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("UserTaskId");
|
||||
});
|
||||
|
||||
b.Navigation("Evidence");
|
||||
|
||||
b.Navigation("Progress")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Verification");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.CheckInAggregate.UserCheckIn", b =>
|
||||
{
|
||||
b.Navigation("CheckInDays");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.Mission", b =>
|
||||
{
|
||||
b.Navigation("Rules");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
|
||||
|
||||
namespace MissionService.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "frequency_types",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false),
|
||||
name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_frequency_types", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "mission_categories",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false),
|
||||
name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_mission_categories", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "mission_statuses",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false),
|
||||
name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_mission_statuses", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "mission_types",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false),
|
||||
name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_mission_types", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "missions",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
title_en = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
title_vi = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
description_en = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||
description_vi = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||
type_id = table.Column<int>(type: "integer", nullable: false),
|
||||
category_id = table.Column<int>(type: "integer", nullable: false),
|
||||
reward_points = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
reward_mining_boost = table.Column<decimal>(type: "numeric(5,2)", precision: 5, scale: 2, nullable: false),
|
||||
reward_xp = table.Column<int>(type: "integer", nullable: false),
|
||||
reward_badge_id = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||
frequency_id = table.Column<int>(type: "integer", nullable: false),
|
||||
max_completions = table.Column<int>(type: "integer", nullable: false),
|
||||
start_date = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
end_date = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
status_id = table.Column<int>(type: "integer", nullable: false),
|
||||
priority = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_missions", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "task_statuses",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false),
|
||||
name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_task_statuses", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_checkins",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
current_streak = table.Column<int>(type: "integer", nullable: false),
|
||||
longest_streak = table.Column<int>(type: "integer", nullable: false),
|
||||
total_checkins = table.Column<int>(type: "integer", nullable: false),
|
||||
last_checkin_date = table.Column<DateOnly>(type: "date", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_user_checkins", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_rewards",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
source_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
type = table.Column<int>(type: "integer", nullable: false),
|
||||
status = table.Column<int>(type: "integer", nullable: false),
|
||||
points = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
bonus_points = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
currency = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: false, defaultValue: "MP"),
|
||||
earned_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
claimed_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
expires_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_user_rewards", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_tasks",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
mission_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
status_id = table.Column<int>(type: "integer", nullable: false),
|
||||
progress_current = table.Column<int>(type: "integer", nullable: false),
|
||||
progress_target = table.Column<int>(type: "integer", nullable: false),
|
||||
progress_updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
evidence_type = table.Column<int>(type: "integer", nullable: true),
|
||||
evidence_data = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: true),
|
||||
evidence_screenshot_url = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||
evidence_video_url = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||
evidence_captured_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
verification_valid = table.Column<bool>(type: "boolean", nullable: true),
|
||||
verification_failure_reason = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||
verification_method = table.Column<int>(type: "integer", nullable: true),
|
||||
verification_by = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
verification_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
reward_claimed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
started_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
completed_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
claimed_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_user_tasks", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "mission_rules",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
rule_type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||
@operator = table.Column<string>(name: "operator", type: "character varying(20)", maxLength: 20, nullable: false),
|
||||
value = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
||||
metadata = table.Column<string>(type: "jsonb", nullable: true),
|
||||
mission_id = table.Column<Guid>(type: "uuid", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_mission_rules", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "FK_mission_rules_missions_mission_id",
|
||||
column: x => x.mission_id,
|
||||
principalTable: "missions",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "checkin_days",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
date = table.Column<DateOnly>(type: "date", nullable: false),
|
||||
points_earned = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
is_milestone = table.Column<bool>(type: "boolean", nullable: false),
|
||||
streak_on_day = table.Column<int>(type: "integer", nullable: false),
|
||||
user_checkin_id = table.Column<Guid>(type: "uuid", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_checkin_days", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "FK_checkin_days_user_checkins_user_checkin_id",
|
||||
column: x => x.user_checkin_id,
|
||||
principalTable: "user_checkins",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "frequency_types",
|
||||
columns: new[] { "id", "name" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ 1, "Once" },
|
||||
{ 2, "Daily" },
|
||||
{ 3, "Weekly" },
|
||||
{ 4, "Unlimited" }
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "mission_categories",
|
||||
columns: new[] { "id", "name" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ 1, "Daily" },
|
||||
{ 2, "Weekly" },
|
||||
{ 3, "Special" },
|
||||
{ 4, "Onboarding" },
|
||||
{ 5, "Event" }
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "mission_statuses",
|
||||
columns: new[] { "id", "name" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ 1, "Draft" },
|
||||
{ 2, "Active" },
|
||||
{ 3, "Paused" },
|
||||
{ 4, "Expired" },
|
||||
{ 5, "Archived" }
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "mission_types",
|
||||
columns: new[] { "id", "name" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ 1, "Video" },
|
||||
{ 2, "Click" },
|
||||
{ 3, "Upload" },
|
||||
{ 4, "Invite" },
|
||||
{ 5, "CheckIn" },
|
||||
{ 6, "Social" }
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "task_statuses",
|
||||
columns: new[] { "id", "name" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ 1, "Pending" },
|
||||
{ 2, "InProgress" },
|
||||
{ 3, "PendingVerification" },
|
||||
{ 4, "Completed" },
|
||||
{ 5, "Rejected" },
|
||||
{ 6, "Cancelled" },
|
||||
{ 7, "Expired" }
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_checkin_days_user_checkin_id_date",
|
||||
table: "checkin_days",
|
||||
columns: new[] { "user_checkin_id", "date" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_mission_rules_mission_id",
|
||||
table: "mission_rules",
|
||||
column: "mission_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_missions_code",
|
||||
table: "missions",
|
||||
column: "code",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_user_checkins_user_id",
|
||||
table: "user_checkins",
|
||||
column: "user_id",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_user_rewards_source_id",
|
||||
table: "user_rewards",
|
||||
column: "source_id",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_user_rewards_status_expires_at",
|
||||
table: "user_rewards",
|
||||
columns: new[] { "status", "expires_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_user_rewards_user_id",
|
||||
table: "user_rewards",
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_user_tasks_mission_id",
|
||||
table: "user_tasks",
|
||||
column: "mission_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_user_tasks_user_id",
|
||||
table: "user_tasks",
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_user_tasks_user_id_mission_id",
|
||||
table: "user_tasks",
|
||||
columns: new[] { "user_id", "mission_id" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "checkin_days");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "frequency_types");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "mission_categories");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "mission_rules");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "mission_statuses");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "mission_types");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "task_statuses");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_rewards");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_tasks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_checkins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "missions");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,737 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using MissionService.Infrastructure;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MissionService.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(MissionDbContext))]
|
||||
partial class MissionDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.CheckInAggregate.CheckInDay", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date");
|
||||
|
||||
b.Property<bool>("IsMilestone")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_milestone");
|
||||
|
||||
b.Property<decimal>("PointsEarned")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasColumnName("points_earned");
|
||||
|
||||
b.Property<int>("StreakOnDay")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("streak_on_day");
|
||||
|
||||
b.Property<Guid>("user_checkin_id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("user_checkin_id", "Date")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("checkin_days", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.CheckInAggregate.UserCheckIn", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<int>("CurrentStreak")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("current_streak");
|
||||
|
||||
b.Property<DateOnly?>("LastCheckInDate")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("last_checkin_date");
|
||||
|
||||
b.Property<int>("LongestStreak")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("longest_streak");
|
||||
|
||||
b.Property<int>("TotalCheckIns")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("total_checkins");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("user_checkins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.FrequencyType", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("frequency_types", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Once"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Daily"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Weekly"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Unlimited"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.Mission", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<int>("Category")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("category_id");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("code");
|
||||
|
||||
b.Property<string>("DescriptionEn")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasColumnName("description_en");
|
||||
|
||||
b.Property<string>("DescriptionVi")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasColumnName("description_vi");
|
||||
|
||||
b.Property<DateTime?>("EndDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("end_date");
|
||||
|
||||
b.Property<int>("Frequency")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("frequency_id");
|
||||
|
||||
b.Property<int>("MaxCompletions")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("max_completions");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("priority");
|
||||
|
||||
b.Property<DateTime>("StartDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("start_date");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status_id");
|
||||
|
||||
b.Property<string>("TitleEn")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("title_en");
|
||||
|
||||
b.Property<string>("TitleVi")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("title_vi");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("type_id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("missions", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("mission_categories", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Daily"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Weekly"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Special"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Onboarding"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Event"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionRule", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Metadata")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("metadata");
|
||||
|
||||
b.Property<string>("Operator")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("operator");
|
||||
|
||||
b.Property<string>("RuleType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("rule_type");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("value");
|
||||
|
||||
b.Property<Guid>("mission_id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("mission_id");
|
||||
|
||||
b.ToTable("mission_rules", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionStatus", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("mission_statuses", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Draft"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Active"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Paused"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Expired"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Archived"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionType", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("mission_types", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Video"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Click"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Upload"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Invite"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Name = "CheckIn"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 6,
|
||||
Name = "Social"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.RewardAggregate.UserReward", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime?>("ClaimedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("claimed_at");
|
||||
|
||||
b.Property<DateTime>("EarnedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("earned_at");
|
||||
|
||||
b.Property<DateTime?>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expires_at");
|
||||
|
||||
b.Property<Guid>("SourceId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("source_id");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("type");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SourceId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Status", "ExpiresAt");
|
||||
|
||||
b.ToTable("user_rewards", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.TaskAggregate.TaskStatus", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("task_statuses", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Pending"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
Name = "InProgress"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
Name = "PendingVerification"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Completed"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Rejected"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 6,
|
||||
Name = "Cancelled"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 7,
|
||||
Name = "Expired"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.TaskAggregate.UserTask", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime?>("ClaimedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("claimed_at");
|
||||
|
||||
b.Property<DateTime?>("CompletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("completed_at");
|
||||
|
||||
b.Property<Guid>("MissionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("mission_id");
|
||||
|
||||
b.Property<bool>("RewardClaimed")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("reward_claimed");
|
||||
|
||||
b.Property<DateTime>("StartedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("started_at");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status_id");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MissionId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("UserId", "MissionId");
|
||||
|
||||
b.ToTable("user_tasks", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.CheckInAggregate.CheckInDay", b =>
|
||||
{
|
||||
b.HasOne("MissionService.Domain.AggregatesModel.CheckInAggregate.UserCheckIn", null)
|
||||
.WithMany("CheckInDays")
|
||||
.HasForeignKey("user_checkin_id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.Mission", b =>
|
||||
{
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.MissionAggregate.MissionReward", "Reward", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("MissionId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<string>("BadgeId")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("reward_badge_id");
|
||||
|
||||
b1.Property<int>("ExperiencePoints")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("reward_xp");
|
||||
|
||||
b1.Property<decimal>("MiningBoostPercent")
|
||||
.HasPrecision(5, 2)
|
||||
.HasColumnType("numeric(5,2)")
|
||||
.HasColumnName("reward_mining_boost");
|
||||
|
||||
b1.Property<decimal>("Points")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasColumnName("reward_points");
|
||||
|
||||
b1.HasKey("MissionId");
|
||||
|
||||
b1.ToTable("missions");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("MissionId");
|
||||
});
|
||||
|
||||
b.Navigation("Reward")
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.MissionRule", b =>
|
||||
{
|
||||
b.HasOne("MissionService.Domain.AggregatesModel.MissionAggregate.Mission", null)
|
||||
.WithMany("Rules")
|
||||
.HasForeignKey("mission_id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.RewardAggregate.UserReward", b =>
|
||||
{
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.RewardAggregate.RewardAmount", "Amount", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("UserRewardId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<decimal>("BonusPoints")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasColumnName("bonus_points");
|
||||
|
||||
b1.Property<string>("Currency")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasDefaultValue("MP")
|
||||
.HasColumnName("currency");
|
||||
|
||||
b1.Property<decimal>("Points")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)")
|
||||
.HasColumnName("points");
|
||||
|
||||
b1.HasKey("UserRewardId");
|
||||
|
||||
b1.ToTable("user_rewards");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("UserRewardId");
|
||||
});
|
||||
|
||||
b.Navigation("Amount")
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.TaskAggregate.UserTask", b =>
|
||||
{
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.TaskAggregate.TaskEvidence", "Evidence", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("UserTaskId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<DateTime>("CapturedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("evidence_captured_at");
|
||||
|
||||
b1.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)")
|
||||
.HasColumnName("evidence_data");
|
||||
|
||||
b1.Property<string>("ScreenshotUrl")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("evidence_screenshot_url");
|
||||
|
||||
b1.Property<int>("Type")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("evidence_type");
|
||||
|
||||
b1.Property<string>("VideoUrl")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("evidence_video_url");
|
||||
|
||||
b1.HasKey("UserTaskId");
|
||||
|
||||
b1.ToTable("user_tasks");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("UserTaskId");
|
||||
});
|
||||
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.TaskAggregate.TaskProgress", "Progress", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("UserTaskId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<int>("CurrentValue")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("progress_current");
|
||||
|
||||
b1.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("progress_updated_at");
|
||||
|
||||
b1.Property<int>("TargetValue")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("progress_target");
|
||||
|
||||
b1.HasKey("UserTaskId");
|
||||
|
||||
b1.ToTable("user_tasks");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("UserTaskId");
|
||||
});
|
||||
|
||||
b.OwnsOne("MissionService.Domain.AggregatesModel.TaskAggregate.VerificationResult", "Verification", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("UserTaskId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<string>("FailureReason")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("verification_failure_reason");
|
||||
|
||||
b1.Property<bool>("IsValid")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("verification_valid");
|
||||
|
||||
b1.Property<int>("Method")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("verification_method");
|
||||
|
||||
b1.Property<DateTime>("VerifiedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("verification_at");
|
||||
|
||||
b1.Property<Guid?>("VerifiedBy")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("verification_by");
|
||||
|
||||
b1.HasKey("UserTaskId");
|
||||
|
||||
b1.ToTable("user_tasks");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("UserTaskId");
|
||||
});
|
||||
|
||||
b.Navigation("Evidence");
|
||||
|
||||
b.Navigation("Progress")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Verification");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.CheckInAggregate.UserCheckIn", b =>
|
||||
{
|
||||
b.Navigation("CheckInDays");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MissionService.Domain.AggregatesModel.MissionAggregate.Mission", b =>
|
||||
{
|
||||
b.Navigation("Rules");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user