feat: Add Chatika integration and Club Features system documentation
- Implement Chatika account activation via background job - Create IChatikaApiService interface and its implementation - Add Club Features system documentation detailing features and entities - Introduce ClubFeatureType enum to replace hardcoded IDs - Update SQL scripts for Club Membership migration - Fix various bugs in BackOffice UI and improve Products page functionality
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
# 🎁 Club Features System
|
||||
|
||||
> **آخرین بروزرسانی**: ۳ دی ۱۴۰۴ (23 December 2025)
|
||||
> **وضعیت**: ✅ Production Ready
|
||||
|
||||
---
|
||||
|
||||
## 📋 فهرست
|
||||
|
||||
1. [معرفی](#معرفی)
|
||||
2. [فیچرهای باشگاه](#فیچرهای-باشگاه)
|
||||
3. [Entity ها](#entity-ها)
|
||||
4. [Enum ClubFeatureType](#enum-clubfeaturetype)
|
||||
5. [فرآیند فعالسازی](#فرآیند-فعالسازی)
|
||||
6. [API ها](#api-ها)
|
||||
|
||||
---
|
||||
|
||||
## معرفی
|
||||
|
||||
سیستم فیچرهای باشگاه مشتریان، امکانات ویژهای را برای اعضای باشگاه فراهم میکند. هر کاربر با فعالسازی باشگاه، به تمام 4 فیچر دسترسی پیدا میکند.
|
||||
|
||||
---
|
||||
|
||||
## فیچرهای باشگاه
|
||||
|
||||
| Id | نام | عنوان فارسی | توضیح |
|
||||
|----|-----|-------------|-------|
|
||||
| 1 | **Chatika** | چتیکا | دستیار هوش مصنوعی - حساب خودکار ایجاد میشود |
|
||||
| 2 | **Bime** | بیمه | خدمات بیمهای |
|
||||
| 3 | **Trip** | تریپ | خدمات سفر و گردشگری |
|
||||
| 4 | **Learn** | لرن | آموزش و یادگیری |
|
||||
|
||||
---
|
||||
|
||||
## Entity ها
|
||||
|
||||
### ClubFeature (تعریف فیچرها)
|
||||
|
||||
```csharp
|
||||
public class ClubFeature : BaseAuditableEntity
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public virtual ICollection<UserClubFeature>? UserClubFeatures { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### UserClubFeature (فیچرهای کاربر)
|
||||
|
||||
```csharp
|
||||
public class UserClubFeature : BaseAuditableEntity
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public virtual User User { get; set; }
|
||||
|
||||
public long ClubMembershipId { get; set; }
|
||||
public virtual ClubMembership ClubMembership { get; set; }
|
||||
|
||||
public long ClubFeatureId { get; set; }
|
||||
public virtual ClubFeature ClubFeature { get; set; }
|
||||
|
||||
public DateTime GrantedAt { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public string? Notes { get; set; } // توضیحات اختیاری یا وضعیت فعالسازی
|
||||
}
|
||||
```
|
||||
|
||||
### Database Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE ClubFeatures (
|
||||
Id BIGINT PRIMARY KEY IDENTITY,
|
||||
Title NVARCHAR(200) NOT NULL,
|
||||
Description NVARCHAR(MAX),
|
||||
IsActive BIT DEFAULT 1,
|
||||
SortOrder INT DEFAULT 0,
|
||||
-- BaseAuditableEntity fields
|
||||
Created DATETIME2,
|
||||
CreatedBy NVARCHAR(100),
|
||||
LastModified DATETIME2,
|
||||
LastModifiedBy NVARCHAR(100),
|
||||
IsDeleted BIT DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE UserClubFeatures (
|
||||
Id BIGINT PRIMARY KEY IDENTITY,
|
||||
UserId BIGINT NOT NULL FOREIGN KEY REFERENCES Users(Id),
|
||||
ClubMembershipId BIGINT NOT NULL FOREIGN KEY REFERENCES ClubMemberships(Id),
|
||||
ClubFeatureId BIGINT NOT NULL FOREIGN KEY REFERENCES ClubFeatures(Id),
|
||||
GrantedAt DATETIME2 NOT NULL,
|
||||
IsActive BIT DEFAULT 1,
|
||||
Notes NVARCHAR(MAX),
|
||||
-- BaseAuditableEntity fields
|
||||
Created DATETIME2,
|
||||
CreatedBy NVARCHAR(100),
|
||||
LastModified DATETIME2,
|
||||
LastModifiedBy NVARCHAR(100),
|
||||
IsDeleted BIT DEFAULT 0
|
||||
);
|
||||
|
||||
-- Seed Data
|
||||
INSERT INTO ClubFeatures (Id, Title, Description, IsActive, SortOrder)
|
||||
VALUES
|
||||
(1, N'چتیکا', N'دستیار هوش مصنوعی', 1, 1),
|
||||
(2, N'بیمه', N'خدمات بیمهای', 1, 2),
|
||||
(3, N'تریپ', N'خدمات سفر و گردشگری', 1, 3),
|
||||
(4, N'لرن', N'آموزش و یادگیری', 1, 4);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Enum ClubFeatureType
|
||||
|
||||
برای جلوگیری از hardcoded IDs، از Enum استفاده میشود:
|
||||
|
||||
**فایل**: `CMSMicroservice.Domain/Enums/ClubFeatureType.cs`
|
||||
|
||||
```csharp
|
||||
namespace CMSMicroservice.Domain.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// انواع ویژگیهای باشگاه مشتریان
|
||||
/// </summary>
|
||||
public enum ClubFeatureType
|
||||
{
|
||||
/// <summary>
|
||||
/// چتیکا - دستیار هوش مصنوعی
|
||||
/// </summary>
|
||||
Chatika = 1,
|
||||
|
||||
/// <summary>
|
||||
/// بیمه - خدمات بیمهای
|
||||
/// </summary>
|
||||
Bime = 2,
|
||||
|
||||
/// <summary>
|
||||
/// تریپ - خدمات سفر و گردشگری
|
||||
/// </summary>
|
||||
Trip = 3,
|
||||
|
||||
/// <summary>
|
||||
/// لرن - آموزش و یادگیری
|
||||
/// </summary>
|
||||
Learn = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods برای ClubFeatureType
|
||||
/// </summary>
|
||||
public static class ClubFeatureTypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// دریافت تمام مقادیر ClubFeatureType به صورت آرایه long
|
||||
/// </summary>
|
||||
public static long[] GetAllFeatureIds()
|
||||
{
|
||||
return Enum.GetValues<ClubFeatureType>()
|
||||
.Select(f => (long)f)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت عنوان فارسی ویژگی
|
||||
/// </summary>
|
||||
public static string GetPersianTitle(this ClubFeatureType featureType)
|
||||
{
|
||||
return featureType switch
|
||||
{
|
||||
ClubFeatureType.Chatika => "چتیکا",
|
||||
ClubFeatureType.Bime => "بیمه",
|
||||
ClubFeatureType.Trip => "تور و سفر",
|
||||
ClubFeatureType.Learn => "آموزش",
|
||||
_ => featureType.ToString()
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### استفاده در کد
|
||||
|
||||
```csharp
|
||||
// ❌ قبل - Hardcoded
|
||||
var featureIds = new long[] { 1, 2, 3, 4 };
|
||||
|
||||
// ✅ بعد - با Enum
|
||||
var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
|
||||
|
||||
// دسترسی به یک فیچر خاص
|
||||
var chatikaId = (long)ClubFeatureType.Chatika; // = 1
|
||||
var title = ClubFeatureType.Bime.GetPersianTitle(); // = "بیمه"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## فرآیند فعالسازی
|
||||
|
||||
هنگام فعالسازی باشگاه مشتریان، فیچرها به این ترتیب اختصاص داده میشوند:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ActivateClubMembershipCommandHandler │
|
||||
│ یا │
|
||||
│ AcceptClubMembershipContractCommandHandler │
|
||||
└─────────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ // 8. اختصاص فیچرهای باشگاه │
|
||||
│ var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds(); │
|
||||
│ foreach (var featureId in featureIds) │
|
||||
│ { │
|
||||
│ _context.UserClubFeatures.Add(new UserClubFeature │
|
||||
│ { │
|
||||
│ UserId = user.Id, │
|
||||
│ ClubMembershipId = membership.Id, │
|
||||
│ ClubFeatureId = featureId, │
|
||||
│ GrantedAt = DateTime.Now, │
|
||||
│ IsActive = true, │
|
||||
│ Notes = null // برای چتیکا بعداً توسط Worker پر میشود │
|
||||
│ }); │
|
||||
│ } │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 4 UserClubFeature Records │
|
||||
│ ┌─────────────┬──────────────┬────────────┬─────────────┐ │
|
||||
│ │ ClubFeatureId │ GrantedAt │ IsActive │ Notes │ │
|
||||
│ ├─────────────┼──────────────┼────────────┼─────────────┤ │
|
||||
│ │ 1 (Chatika) │ 2025-12-23 │ true │ NULL → پر │ │
|
||||
│ │ 2 (Bime) │ 2025-12-23 │ true │ NULL │ │
|
||||
│ │ 3 (Trip) │ 2025-12-23 │ true │ NULL │ │
|
||||
│ │ 4 (Learn) │ 2025-12-23 │ true │ NULL │ │
|
||||
│ └─────────────┴──────────────┴────────────┴─────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼ (برای چتیکا)
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ChatikaAccountActivationJob (Worker) │
|
||||
│ - هر 5 دقیقه اجرا میشود │
|
||||
│ - کاربران با Notes = NULL و ClubFeatureId = 1 را پیدا میکند │
|
||||
│ - API چتیکا را کال میکند │
|
||||
│ - Notes را با توضیحات فارسی پر میکند │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API ها
|
||||
|
||||
### GetUserClubFeatures
|
||||
|
||||
دریافت لیست فیچرهای فعال کاربر:
|
||||
|
||||
```protobuf
|
||||
rpc GetUserClubFeatures (GetUserClubFeaturesRequest) returns (GetUserClubFeaturesResponse);
|
||||
|
||||
message GetUserClubFeaturesRequest {
|
||||
int64 user_id = 1;
|
||||
}
|
||||
|
||||
message GetUserClubFeaturesResponse {
|
||||
repeated UserClubFeatureModel features = 1;
|
||||
}
|
||||
|
||||
message UserClubFeatureModel {
|
||||
int64 id = 1;
|
||||
int64 club_feature_id = 2;
|
||||
string feature_title = 3;
|
||||
string feature_description = 4;
|
||||
google.protobuf.Timestamp granted_at = 5;
|
||||
bool is_active = 6;
|
||||
string notes = 7;
|
||||
}
|
||||
```
|
||||
|
||||
### ToggleUserClubFeature
|
||||
|
||||
فعال/غیرفعال کردن فیچر توسط ادمین:
|
||||
|
||||
```protobuf
|
||||
rpc ToggleUserClubFeature (ToggleUserClubFeatureRequest) returns (ToggleUserClubFeatureResponse);
|
||||
|
||||
message ToggleUserClubFeatureRequest {
|
||||
int64 user_club_feature_id = 1;
|
||||
bool is_active = 2;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Query های مفید
|
||||
|
||||
### تعداد فیچرهای فعال هر کاربر
|
||||
|
||||
```sql
|
||||
SELECT u.Mobile, COUNT(ucf.Id) as FeatureCount
|
||||
FROM Users u
|
||||
JOIN UserClubFeatures ucf ON u.Id = ucf.UserId
|
||||
WHERE ucf.IsActive = 1 AND ucf.IsDeleted = 0
|
||||
GROUP BY u.Mobile
|
||||
```
|
||||
|
||||
### کاربران بدون فیچر چتیکا فعال
|
||||
|
||||
```sql
|
||||
SELECT u.Id, u.Mobile
|
||||
FROM Users u
|
||||
JOIN ClubMemberships cm ON u.Id = cm.UserId
|
||||
WHERE cm.IsActive = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM UserClubFeatures ucf
|
||||
WHERE ucf.UserId = u.Id
|
||||
AND ucf.ClubFeatureId = 1
|
||||
AND ucf.IsActive = 1
|
||||
)
|
||||
```
|
||||
|
||||
### وضعیت فعالسازی چتیکا
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
CASE WHEN Notes IS NOT NULL THEN 'Activated' ELSE 'Pending' END as Status,
|
||||
COUNT(*) as Count
|
||||
FROM UserClubFeatures
|
||||
WHERE ClubFeatureId = 1 AND IsDeleted = 0
|
||||
GROUP BY CASE WHEN Notes IS NOT NULL THEN 'Activated' ELSE 'Pending' END
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 مستندات مرتبط
|
||||
|
||||
- [Chatika Integration](./chatika-integration.md)
|
||||
- [Club Membership Migration](./club-membership-migration.md)
|
||||
- [Commission System](./commission-system.md)
|
||||
Reference in New Issue
Block a user