feat: Add customer management, payments, and push notifications with security enhancements
Some checks failed
Build and Deploy LittleShop / Build TeleBot Docker Image (push) Failing after 11s
Build and Deploy LittleShop / Build LittleShop Docker Image (push) Failing after 15s
Build and Deploy LittleShop / Deploy to Production VPS (Manual Only) (push) Has been skipped
Build and Deploy LittleShop / Deploy to Pre-Production (CT109) (push) Has been skipped

Major Feature Additions:
- Customer management: Full CRUD with data export and privacy compliance
- Payment management: Centralized payment tracking and administration
- Push notification subscriptions: Manage and track web push subscriptions

Security Enhancements:
- IP whitelist middleware for administrative endpoints
- Data retention service with configurable policies
- Enhanced push notification security documentation
- Security fixes progress tracking (2025-11-14)

UI/UX Improvements:
- Enhanced navigation with improved mobile responsiveness
- Updated admin dashboard with order status counts
- Improved product CRUD forms
- New customer and payment management interfaces

Backend Improvements:
- Extended customer service with data export capabilities
- Enhanced order service with status count queries
- Improved crypto payment service with better error handling
- Updated validators and configuration

Documentation:
- DEPLOYMENT_NGINX_GUIDE.md: Nginx deployment instructions
- IP_STORAGE_ANALYSIS.md: IP storage security analysis
- PUSH_NOTIFICATION_SECURITY.md: Push notification security guide
- UI_UX_IMPROVEMENT_PLAN.md: Planned UI/UX enhancements
- UI_UX_IMPROVEMENTS_COMPLETED.md: Completed improvements

Cleanup:
- Removed temporary database WAL files
- Removed stale commit message file

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-16 19:33:02 +00:00
parent 47e43d4ff8
commit a2247d7c02
45 changed files with 5302 additions and 371 deletions

View File

@@ -293,4 +293,136 @@ public class CustomerService : ICustomerService
_logger.LogInformation("Unblocked customer {CustomerId}", customerId);
return true;
}
public async Task<CustomerDataExportDto?> GetCustomerDataForExportAsync(Guid customerId)
{
try
{
// Get customer with all related data
// Note: EF Core requires AsSplitQuery for multiple ThenInclude on same level
var customer = await _context.Customers
.Include(c => c.Orders)
.ThenInclude(o => o.Items)
.ThenInclude(oi => oi.Product)
.Include(c => c.Orders)
.ThenInclude(o => o.Items)
.ThenInclude(oi => oi.ProductVariant)
.Include(c => c.Messages)
.AsSplitQuery()
.FirstOrDefaultAsync(c => c.Id == customerId);
if (customer == null)
{
_logger.LogWarning("Customer {CustomerId} not found for data export", customerId);
return null;
}
// Get customer reviews separately (no direct navigation property from Customer to Review)
var reviews = await _context.Reviews
.Include(r => r.Product)
.Where(r => r.CustomerId == customerId)
.ToListAsync();
// Build export DTO
var exportDto = new CustomerDataExportDto
{
// Customer Profile
CustomerId = customer.Id,
TelegramUserId = customer.TelegramUserId,
TelegramUsername = customer.TelegramUsername,
TelegramDisplayName = customer.TelegramDisplayName,
TelegramFirstName = customer.TelegramFirstName,
TelegramLastName = customer.TelegramLastName,
Email = customer.Email,
PhoneNumber = customer.PhoneNumber,
// Preferences
AllowMarketing = customer.AllowMarketing,
AllowOrderUpdates = customer.AllowOrderUpdates,
Language = customer.Language,
Timezone = customer.Timezone,
// Metrics
TotalOrders = customer.TotalOrders,
TotalSpent = customer.TotalSpent,
AverageOrderValue = customer.AverageOrderValue,
FirstOrderDate = customer.FirstOrderDate == DateTime.MinValue ? null : customer.FirstOrderDate,
LastOrderDate = customer.LastOrderDate == DateTime.MinValue ? null : customer.LastOrderDate,
// Account Status
IsBlocked = customer.IsBlocked,
BlockReason = customer.BlockReason,
RiskScore = customer.RiskScore,
CustomerNotes = customer.CustomerNotes,
// Timestamps
CreatedAt = customer.CreatedAt,
UpdatedAt = customer.UpdatedAt,
LastActiveAt = customer.LastActiveAt,
DataRetentionDate = customer.DataRetentionDate,
// Orders
Orders = customer.Orders.Select(o => new CustomerOrderExportDto
{
OrderId = o.Id,
Status = o.Status.ToString(),
TotalAmount = o.TotalAmount,
Currency = o.Currency,
OrderDate = o.CreatedAt,
ShippingName = o.ShippingName,
ShippingAddress = o.ShippingAddress,
ShippingCity = o.ShippingCity,
ShippingPostCode = o.ShippingPostCode,
ShippingCountry = o.ShippingCountry,
TrackingNumber = o.TrackingNumber,
EstimatedDeliveryDate = o.ExpectedDeliveryDate,
ActualDeliveryDate = o.ActualDeliveryDate,
Notes = o.Notes,
Items = o.Items.Select(oi => new CustomerOrderItemExportDto
{
ProductName = oi.Product?.Name ?? "Unknown Product",
VariantName = oi.ProductVariant?.Name,
Quantity = oi.Quantity,
UnitPrice = oi.UnitPrice,
TotalPrice = oi.TotalPrice
}).ToList()
}).ToList(),
// Messages
Messages = customer.Messages.Select(m => new CustomerMessageExportDto
{
SentAt = m.SentAt ?? m.CreatedAt,
MessageType = m.Type.ToString(),
Content = m.Content,
WasRead = m.Status == LittleShop.Models.MessageStatus.Read,
ReadAt = m.ReadAt
}).ToList(),
// Reviews
Reviews = reviews.Select(r => new CustomerReviewExportDto
{
ProductId = r.ProductId,
ProductName = r.Product?.Name ?? "Unknown Product",
Rating = r.Rating,
Comment = r.Comment,
CreatedAt = r.CreatedAt,
IsApproved = r.IsApproved,
IsVerifiedPurchase = r.IsVerifiedPurchase
}).ToList()
};
_logger.LogInformation("Generated data export for customer {CustomerId} with {OrderCount} orders, {MessageCount} messages, {ReviewCount} reviews",
customerId, exportDto.Orders.Count, exportDto.Messages.Count, exportDto.Reviews.Count);
return exportDto;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating data export for customer {CustomerId}", customerId);
return null;
}
}
}