docs: consolidate 53 files into 15 structured files in 3 folders
- business/ (5): club-commission, payment, ecommerce, membership, content - technical/ (5): cms-arch, ui, deployment, migration, api - overview/ (5): flowcharts, index, changelog, glossary, roadmap - Removed all old folders: backoffice, cms, deployment, docs, frontoffice, migration, ui-modernization, business (old) - Updated internal links with relative folder paths
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
# 🔌 API، Protobuf و یکپارچهسازی خارجی
|
||||
|
||||
> **منابع ادغامشده:** `FRONTOFFICE-CMS-API-COMPATIBILITY.md`, `REMAINING-TASKS.md`, `chatika-integration.md`, `payment-gateway.md`, `club-feature-management-services.md`
|
||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴
|
||||
|
||||
---
|
||||
|
||||
## ۱. معماری ارتباطات
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ External Services │
|
||||
│ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │ ZarinPal│ │ Kavenegar│ │ DayaLoan│ │ Chatika │ │
|
||||
│ │ (IPG) │ │ (SMS) │ │ (Loan) │ │ (AI) │ │
|
||||
│ └────┬────┘ └────┬─────┘ └────┬────┘ └────┬────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ ┌────▼───────────▼────────────▼────────────▼────┐ │
|
||||
│ │ CMS Microservice │ │
|
||||
│ │ (gRPC Server + Hangfire + EF Core) │ │
|
||||
│ └────────────────┬───────────────────────────────┘ │
|
||||
│ │ gRPC (Protobuf v3) │
|
||||
│ ┌───────────┼───────────┐ │
|
||||
│ ┌────▼────┐ ┌────▼────┐ │
|
||||
│ │BackOffice│ │FrontOffice│ │
|
||||
│ │(Blazor │ │(Blazor │ │
|
||||
│ │ WASM) │ │ Server) │ │
|
||||
│ └─────────┘ └──────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۲. gRPC Proto Definitions
|
||||
|
||||
### ۲.۱ لیست کامل سرویسها
|
||||
|
||||
```protobuf
|
||||
// ===== product.proto =====
|
||||
service ProductService {
|
||||
rpc GetProducts (GetProductsRequest) returns (GetProductsResponse);
|
||||
rpc GetProductById (GetProductByIdRequest) returns (ProductMessage);
|
||||
rpc CreateProduct (CreateProductRequest) returns (CreateProductResponse);
|
||||
rpc UpdateProduct (UpdateProductRequest) returns (UpdateProductResponse);
|
||||
rpc DeleteProduct (DeleteProductRequest) returns (Empty);
|
||||
rpc GetProductsPaged (GetProductsPagedRequest) returns (GetProductsPagedResponse);
|
||||
}
|
||||
|
||||
// ===== order.proto =====
|
||||
service OrderService {
|
||||
rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse);
|
||||
rpc GetOrders (GetOrdersRequest) returns (GetOrdersResponse);
|
||||
rpc GetOrderById (GetOrderByIdRequest) returns (OrderMessage);
|
||||
rpc UpdateOrderStatus (UpdateOrderStatusRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// ===== user.proto =====
|
||||
service UserService {
|
||||
rpc Register (RegisterRequest) returns (AuthResponse);
|
||||
rpc Login (LoginRequest) returns (AuthResponse);
|
||||
rpc GetProfile (GetProfileRequest) returns (UserProfileMessage);
|
||||
rpc UpdateProfile (UpdateProfileRequest) returns (Empty);
|
||||
rpc SendOtp (SendOtpRequest) returns (SendOtpResponse);
|
||||
rpc VerifyOtp (VerifyOtpRequest) returns (VerifyOtpResponse);
|
||||
}
|
||||
|
||||
// ===== club.proto =====
|
||||
service ClubService {
|
||||
rpc GetNetworkTree (GetNetworkTreeRequest) returns (NetworkTreeResponse);
|
||||
rpc GetBalance (GetBalanceRequest) returns (BalanceResponse);
|
||||
rpc ReadContract (ReadContractRequest) returns (ContractResponse);
|
||||
rpc RequestContractOtp (RequestOtpRequest) returns (OtpResponse);
|
||||
rpc VerifyContractOtp (VerifyOtpRequest) returns (VerifyOtpResponse);
|
||||
rpc AcceptContract (AcceptContractRequest) returns (AcceptContractResponse);
|
||||
rpc GetClubFeatures (GetFeaturesRequest) returns (FeaturesResponse);
|
||||
}
|
||||
|
||||
// ===== payment.proto =====
|
||||
service PaymentService {
|
||||
rpc CreatePayment (CreatePaymentRequest) returns (CreatePaymentResponse);
|
||||
rpc VerifyPayment (VerifyPaymentRequest) returns (VerifyPaymentResponse);
|
||||
rpc GetPaymentStatus (PaymentStatusRequest) returns (PaymentStatusResponse);
|
||||
}
|
||||
|
||||
// ===== blog.proto =====
|
||||
service BlogService {
|
||||
rpc GetPosts (GetPostsRequest) returns (GetPostsResponse);
|
||||
rpc GetPostBySlug (GetPostBySlugRequest) returns (BlogPostMessage);
|
||||
rpc CreatePost (CreatePostRequest) returns (CreatePostResponse);
|
||||
rpc UpdatePost (UpdatePostRequest) returns (Empty);
|
||||
rpc DeletePost (DeletePostRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// ===== inventory.proto =====
|
||||
service InventoryService {
|
||||
rpc GetInventory (GetInventoryRequest) returns (InventoryMessage);
|
||||
rpc UpdateStock (UpdateStockRequest) returns (Empty);
|
||||
rpc GetAllInventories (GetAllRequest) returns (InventoryListResponse);
|
||||
}
|
||||
|
||||
// ===== sitepage.proto =====
|
||||
service SitePageService {
|
||||
rpc GetPage (GetPageRequest) returns (SitePageMessage);
|
||||
rpc SaveSettings (SaveSettingsRequest) returns (Empty);
|
||||
rpc GetAllPages (Empty) returns (PageListResponse);
|
||||
}
|
||||
|
||||
// ===== file.proto =====
|
||||
service FileService {
|
||||
rpc Upload (stream UploadRequest) returns (UploadResponse);
|
||||
rpc Download (DownloadRequest) returns (stream DownloadResponse);
|
||||
rpc Delete (DeleteFileRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// ===== category.proto =====
|
||||
service CategoryService {
|
||||
rpc GetCategories (GetCategoriesRequest) returns (CategoryListResponse);
|
||||
rpc CreateCategory (CreateCategoryRequest) returns (CreateCategoryResponse);
|
||||
rpc UpdateCategory (UpdateCategoryRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// ===== config.proto =====
|
||||
service SystemConfigService {
|
||||
rpc GetConfig (GetConfigRequest) returns (ConfigResponse);
|
||||
rpc UpdateConfig (UpdateConfigRequest) returns (Empty);
|
||||
rpc GetAllConfigs (Empty) returns (ConfigListResponse);
|
||||
}
|
||||
```
|
||||
|
||||
### ۲.۲ Shared Messages
|
||||
|
||||
```protobuf
|
||||
// ===== common.proto =====
|
||||
message PaginationState {
|
||||
int32 skip = 1;
|
||||
int32 take = 2;
|
||||
}
|
||||
|
||||
message PaginatedResponse {
|
||||
int32 totalCount = 1;
|
||||
int32 pageSize = 2;
|
||||
int32 currentPage = 3;
|
||||
}
|
||||
|
||||
message Empty {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۳. External Service Integration
|
||||
|
||||
### ۳.۱ ZarinPal (پرداخت)
|
||||
|
||||
```csharp
|
||||
public class ZarinPalService : IPaymentGateway
|
||||
{
|
||||
// Config
|
||||
private readonly string _merchantId;
|
||||
private readonly bool _isSandbox;
|
||||
|
||||
// Endpoints
|
||||
const string PAYMENT_URL = "https://api.zarinpal.com/pg/v4/payment/request.json";
|
||||
const string VERIFY_URL = "https://api.zarinpal.com/pg/v4/payment/verify.json";
|
||||
const string SANDBOX_URL = "https://sandbox.zarinpal.com/pg/v4/payment/request.json";
|
||||
|
||||
// Flow
|
||||
// 1. CreatePayment → Authority token
|
||||
// 2. Redirect → https://www.zarinpal.com/pg/StartPay/{Authority}
|
||||
// 3. Callback → VerifyPayment(Authority, Amount)
|
||||
// 4. Result → RefID (reference number)
|
||||
}
|
||||
```
|
||||
|
||||
### ۳.۲ Kavenegar (SMS)
|
||||
|
||||
```csharp
|
||||
public class KavenegarService : ISmsService
|
||||
{
|
||||
// Templates
|
||||
const string OTP_TEMPLATE = "verify-foursat";
|
||||
const string CONTRACT_TEMPLATE = "contract-verify";
|
||||
const string WELCOME_TEMPLATE = "club-welcome";
|
||||
|
||||
// Rate Limiting
|
||||
// ۱ SMS per phone per 60 seconds
|
||||
// ۵ SMS per phone per hour
|
||||
// ۲۰ SMS per phone per day
|
||||
|
||||
public async Task SendOtpAsync(string phone, string code)
|
||||
{
|
||||
await _api.VerifyLookup(phone, code, OTP_TEMPLATE);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ۳.۳ Daya Loan (وام)
|
||||
|
||||
```csharp
|
||||
public class DayaLoanService : ILoanService
|
||||
{
|
||||
// Hangfire job — هر ۱۵ دقیقه
|
||||
// Polly retry: 3 attempts, exponential backoff (2s, 4s, 8s)
|
||||
// Mock mode for staging (auto-approve)
|
||||
|
||||
public async Task<LoanResult> RequestLoanAsync(Guid userId, decimal amount)
|
||||
{
|
||||
if (_options.UseMock)
|
||||
return LoanResult.Approved(amount);
|
||||
|
||||
var response = await _httpClient.PostAsync(
|
||||
$"{_baseUrl}/api/loans/request",
|
||||
new { UserId = userId, Amount = amount });
|
||||
|
||||
return MapResponse(response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ۳.۴ Chatika (AI)
|
||||
|
||||
```csharp
|
||||
public class ChatikaService : IAiChatService
|
||||
{
|
||||
// Hangfire job — هر ۵ دقیقه
|
||||
// Polly retry: 3 attempts
|
||||
// Only for active club members
|
||||
|
||||
public async Task<string> GetResponseAsync(string userMessage)
|
||||
{
|
||||
var response = await _httpClient.PostAsync(
|
||||
$"{_baseUrl}/api/chat",
|
||||
new { Message = userMessage });
|
||||
|
||||
return response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۴. API Compatibility Layer
|
||||
|
||||
### ۴.۱ FrontOffice Service Pattern
|
||||
|
||||
```csharp
|
||||
// هر سرویس در FrontOffice یک wrapper بر gRPC client است
|
||||
public class ProductService : IProductService
|
||||
{
|
||||
private readonly ProductServiceClient _client;
|
||||
|
||||
public ProductService(ProductServiceClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public async Task<ProductListResult> GetProductsPagedAsync(
|
||||
int skip, int take, Guid? categoryId = null, string? search = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new GetProductsPagedRequest {
|
||||
Pagination = new PaginationState { Skip = skip, Take = take },
|
||||
CategoryId = categoryId?.ToString() ?? "",
|
||||
SearchTerm = search ?? ""
|
||||
};
|
||||
|
||||
var response = await _client.GetProductsPagedAsync(request);
|
||||
|
||||
return new ProductListResult(
|
||||
response.Products.Select(MapToDto).ToList(),
|
||||
response.TotalCount);
|
||||
}
|
||||
catch (RpcException ex) when (ex.StatusCode == StatusCode.Unavailable)
|
||||
{
|
||||
// CMS is down — show cached data or error
|
||||
throw new ServiceUnavailableException("CMS service unavailable");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ۴.۲ Error Handling
|
||||
|
||||
| gRPC Status | HTTP Equivalent | Handling |
|
||||
|------------|-----------------|----------|
|
||||
| `OK` | 200 | Return data |
|
||||
| `NotFound` | 404 | Show "not found" message |
|
||||
| `InvalidArgument` | 400 | Show validation errors |
|
||||
| `Unauthenticated` | 401 | Redirect to login |
|
||||
| `PermissionDenied` | 403 | Show "access denied" |
|
||||
| `Unavailable` | 503 | Show "service down" |
|
||||
| `Internal` | 500 | Show generic error |
|
||||
|
||||
---
|
||||
|
||||
## ۵. Proto Package Distribution
|
||||
|
||||
```
|
||||
CMS/src/Protos/*.proto
|
||||
│
|
||||
▼
|
||||
pack-protos.sh
|
||||
│
|
||||
▼
|
||||
Foursat.CMSMicroservice.Protobuf.nupkg (v1.0.x)
|
||||
│
|
||||
▼
|
||||
Push to BaGet (http://localhost:5555) or Nexus
|
||||
│
|
||||
▼
|
||||
BackOffice: <PackageReference Include="Foursat.CMSMicroservice.Protobuf" />
|
||||
FrontOffice: <PackageReference Include="Foursat.CMSMicroservice.Protobuf" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ۶. Remaining Tasks / Integration Gaps
|
||||
|
||||
| آیتم | اولویت | وضعیت |
|
||||
|------|---------|--------|
|
||||
| Product Bundle API | Medium | ⬜ Proto + Handler needed |
|
||||
| Manual Payment API | Low | ⬜ Design only |
|
||||
| SignalR for Chatika | Low | ⬜ Replace polling |
|
||||
| File upload streaming | Done | ✅ |
|
||||
| Blog search | Done | ✅ |
|
||||
| Inventory autocomplete | Done | ✅ |
|
||||
| Lazy load pagination | Done | ✅ |
|
||||
| Rate limiting (API level) | Medium | ⬜ |
|
||||
| API versioning | Low | ⬜ |
|
||||
Reference in New Issue
Block a user