feat(logging): implement log pruning functionality
- Add LogPruneService to handle the deletion of old logs based on retention settings. - Create PruneLogsCommand to provide a console command for log pruning. - Introduce PruneLogsMessage and PruneLogsHandler for message handling related to log pruning. - Update the AST cache with new classes and their relationships.
This commit is contained in:
+148
-32
@@ -1,12 +1,14 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { api } from '../lib/api';
|
||||
import type { PaginatedResponse } from '../lib/api';
|
||||
import type { PaginatedResponse, ApiResponse } from '../lib/api';
|
||||
import type { AppLog } from '../types';
|
||||
import { formatDateTime } from '../lib/utils';
|
||||
import DataTable, { Column } from '../components/ui/DataTable';
|
||||
import Pagination from '../components/ui/Pagination';
|
||||
import Modal from '../components/ui/Modal';
|
||||
import ConfirmDialog from '../components/ui/ConfirmDialog';
|
||||
import SearchableSelect from '../components/ui/SearchableSelect';
|
||||
|
||||
const LEVEL_META: Record<string, { label: string; cls: string }> = {
|
||||
@@ -27,12 +29,17 @@ const LEVEL_FILTER_OPTIONS = [
|
||||
{ value: 'critical', label: 'بحرانی' },
|
||||
];
|
||||
|
||||
type Tab = 'logs' | 'settings';
|
||||
|
||||
export default function LogsPage() {
|
||||
const [tab, setTab] = useState<Tab>('logs');
|
||||
const [page, setPage] = useState(1);
|
||||
const [level, setLevel] = useState('');
|
||||
const [search, setSearch] = useState('');
|
||||
const [viewLog, setViewLog] = useState<AppLog | null>(null);
|
||||
const [confirmClear, setConfirmClear] = useState(false);
|
||||
const limit = 25;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const logsQuery = useQuery({
|
||||
queryKey: ['admin-logs', page, level, search],
|
||||
@@ -44,6 +51,15 @@ export default function LogsPage() {
|
||||
),
|
||||
});
|
||||
|
||||
const clearMutation = useMutation({
|
||||
mutationFn: () => api.delete<ApiResponse<{ deleted: number }>>('/api/v1/admin/logs'),
|
||||
onSuccess: () => {
|
||||
setConfirmClear(false);
|
||||
setPage(1);
|
||||
queryClient.invalidateQueries({ queryKey: ['admin-logs'] });
|
||||
},
|
||||
});
|
||||
|
||||
const columns: Column<AppLog>[] = [
|
||||
{ key: 'created_at', header: 'زمان', render: (l) => formatDateTime(l.created_at) },
|
||||
{
|
||||
@@ -78,42 +94,71 @@ export default function LogsPage() {
|
||||
<h1 className="section-title">لاگها</h1>
|
||||
<div className="muted">رویدادهای ثبتشدهی سیستم (اخطار و بالاتر)</div>
|
||||
</div>
|
||||
{tab === 'logs' && (
|
||||
<button
|
||||
className="btn danger sm"
|
||||
onClick={() => setConfirmClear(true)}
|
||||
disabled={(logsQuery.data?.meta?.totalRecords ?? 0) === 0}
|
||||
>
|
||||
<TrashIcon style={{ width: 16, height: 16 }} />
|
||||
حذف همه لاگها
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
<div className="card-pad">
|
||||
<div style={{ marginBottom: 12, display: 'flex', gap: 12, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
|
||||
<input
|
||||
className="input"
|
||||
style={{ maxWidth: 260 }}
|
||||
placeholder="جستجو در متن..."
|
||||
value={search}
|
||||
onChange={(e) => { setSearch(e.target.value); setPage(1); }}
|
||||
/>
|
||||
<div style={{ width: 200 }}>
|
||||
<SearchableSelect
|
||||
options={LEVEL_FILTER_OPTIONS}
|
||||
value={level}
|
||||
onChange={(v) => { setLevel(v ? String(v) : ''); setPage(1); }}
|
||||
placeholder="همه سطوح"
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 'var(--gap)' }}>
|
||||
<button
|
||||
className={`btn ${tab === 'logs' ? 'primary' : 'ghost'} sm`}
|
||||
onClick={() => setTab('logs')}
|
||||
>
|
||||
فهرست لاگها
|
||||
</button>
|
||||
<button
|
||||
className={`btn ${tab === 'settings' ? 'primary' : 'ghost'} sm`}
|
||||
onClick={() => setTab('settings')}
|
||||
>
|
||||
مدت نگهداری
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{tab === 'logs' && (
|
||||
<div className="card">
|
||||
<div className="card-pad">
|
||||
<div style={{ marginBottom: 12, display: 'flex', gap: 12, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
|
||||
<input
|
||||
className="input"
|
||||
style={{ maxWidth: 260 }}
|
||||
placeholder="جستجو در متن..."
|
||||
value={search}
|
||||
onChange={(e) => { setSearch(e.target.value); setPage(1); }}
|
||||
/>
|
||||
<div style={{ width: 200 }}>
|
||||
<SearchableSelect
|
||||
options={LEVEL_FILTER_OPTIONS}
|
||||
value={level}
|
||||
onChange={(v) => { setLevel(v ? String(v) : ''); setPage(1); }}
|
||||
placeholder="همه سطوح"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DataTable<AppLog>
|
||||
columns={columns}
|
||||
data={logsQuery.data?.data ?? []}
|
||||
loading={logsQuery.isLoading}
|
||||
emptyMessage="لاگی یافت نشد"
|
||||
/>
|
||||
<Pagination
|
||||
page={page}
|
||||
total={logsQuery.data?.meta?.totalRecords ?? 0}
|
||||
limit={limit}
|
||||
onPageChange={setPage}
|
||||
/>
|
||||
<DataTable<AppLog>
|
||||
columns={columns}
|
||||
data={logsQuery.data?.data ?? []}
|
||||
loading={logsQuery.isLoading}
|
||||
emptyMessage="لاگی یافت نشد"
|
||||
/>
|
||||
<Pagination
|
||||
page={page}
|
||||
total={logsQuery.data?.meta?.totalRecords ?? 0}
|
||||
limit={limit}
|
||||
onPageChange={setPage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === 'settings' && <RetentionSettings />}
|
||||
|
||||
<Modal open={!!viewLog} title="جزئیات لاگ" onClose={() => setViewLog(null)}>
|
||||
{viewLog && (
|
||||
@@ -137,6 +182,77 @@ export default function LogsPage() {
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
<ConfirmDialog
|
||||
open={confirmClear}
|
||||
title="حذف همه لاگها"
|
||||
message="همهی لاگهای سیستم برای همیشه حذف میشوند. این عمل قابل بازگشت نیست. ادامه میدهید؟"
|
||||
confirmLabel="حذف همه"
|
||||
danger
|
||||
loading={clearMutation.isPending}
|
||||
onConfirm={() => clearMutation.mutate()}
|
||||
onCancel={() => setConfirmClear(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RetentionSettings() {
|
||||
const queryClient = useQueryClient();
|
||||
const [days, setDays] = useState<string>('');
|
||||
const [touched, setTouched] = useState(false);
|
||||
|
||||
const settingsQuery = useQuery({
|
||||
queryKey: ['admin-settings'],
|
||||
queryFn: () => api.get<ApiResponse<Record<string, string | boolean>>>('/api/v1/admin/settings'),
|
||||
});
|
||||
|
||||
const current = String(settingsQuery.data?.data?.log_retention_days ?? '');
|
||||
const value = touched ? days : current;
|
||||
|
||||
const saveMutation = useMutation({
|
||||
mutationFn: (retentionDays: string) =>
|
||||
api.patch<ApiResponse<Record<string, string | boolean>>>('/api/v1/admin/settings', {
|
||||
log_retention_days: retentionDays,
|
||||
}),
|
||||
onSuccess: () => {
|
||||
setTouched(false);
|
||||
queryClient.invalidateQueries({ queryKey: ['admin-settings'] });
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-pad">
|
||||
<div style={{ maxWidth: 420, display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<div>
|
||||
<label className="muted" style={{ fontSize: 13, display: 'block', marginBottom: 6 }}>
|
||||
مدت نگهداری لاگها (روز)
|
||||
</label>
|
||||
<input
|
||||
className="input"
|
||||
type="number"
|
||||
min={0}
|
||||
value={value}
|
||||
placeholder={settingsQuery.isLoading ? 'در حال بارگذاری...' : '90'}
|
||||
onChange={(e) => { setDays(e.target.value); setTouched(true); }}
|
||||
style={{ maxWidth: 200 }}
|
||||
/>
|
||||
<div className="muted" style={{ fontSize: 12, marginTop: 6, lineHeight: 1.7 }}>
|
||||
لاگهای قدیمیتر از این مدت بهصورت روزانه بهصورت خودکار حذف میشوند. مقدار ۰ یعنی نگهداری نامحدود.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
className="btn primary sm"
|
||||
disabled={saveMutation.isPending || !touched || value === current}
|
||||
onClick={() => saveMutation.mutate(String(parseInt(value || '0', 10)))}
|
||||
>
|
||||
{saveMutation.isPending ? 'در حال ذخیره...' : 'ذخیره'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ framework:
|
||||
routing:
|
||||
'App\Shared\Message\SendSmsMessage': async
|
||||
'App\Appointment\Message\ExpireAppointmentsMessage': scheduler_default
|
||||
'App\Shared\Logging\Message\PruneLogsMessage': scheduler_default
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
|
||||
@@ -845,6 +845,7 @@ Returns all site configuration values.
|
||||
"support_phone": "",
|
||||
"max_cancel_hours_before": "24",
|
||||
"appointment_reminder_hours": "2",
|
||||
"log_retention_days": "90",
|
||||
"payment_test_mode": "0",
|
||||
"mellat_terminal_id": "",
|
||||
"mellat_username": "",
|
||||
@@ -984,6 +985,7 @@ Reject a pending request. **Permission:** `ROLE_ADMIN`
|
||||
| `tax_percent` | `10` | درصد مالیات |
|
||||
| `sms_panel_fee_rials` | `1500000` | هزینه ثابت پنل پیامک به ریال (از نوبت و اشتراک کسر میشود) |
|
||||
| `appointment_fee_rials` | `150000` | مبلغ هر نوبت به ریال؛ مبلغی که بیمار هنگام رزرو آنلاین پرداخت میکند. backend از همین کلید میخواند و در `GET /api/v1/payment/config` expose میشود |
|
||||
| `log_retention_days` | `90` | مدت نگهداری لاگها (روز)؛ کاماند روزانه `app:prune-logs` لاگهای قدیمیتر را حذف میکند. `0` = نگهداری نامحدود |
|
||||
|
||||
**ترتیب محاسبه** (در `CommissionService`): ۱) کسر `sms_panel_fee_rials` ۲) مالیاتِ استخراجی `afterSms × tax/(100+tax)` ۳) پورسانت = `netAfterTax × percent/100`. سهم نماینده به کیفپولش (`WalletTransaction` credit) واریز و یک ردیف `FinancialBreakdown` ثبت میشود (idempotent بر اساس `payment_id`).
|
||||
|
||||
@@ -1128,3 +1130,24 @@ Ordered by newest first (`id DESC`).
|
||||
Notes:
|
||||
- `context` is a JSON string (or `null`); a `Throwable` in the context is stored as a compact `Class: message @ file:line` string, never the raw object.
|
||||
- `created_at` is a Unix timestamp (integer).
|
||||
|
||||
### DELETE `/api/v1/admin/logs`
|
||||
|
||||
Delete **all** persisted logs (truncate the `app_log` table). Irreversible.
|
||||
|
||||
**Permission:** `ROLE_ADMIN`
|
||||
|
||||
#### Response `200`
|
||||
```json
|
||||
{ "success": true, "data": { "deleted": 137 } }
|
||||
```
|
||||
|
||||
- `deleted` — number of rows removed.
|
||||
|
||||
### Log Retention
|
||||
|
||||
Logs are pruned automatically based on the `log_retention_days` setting (see [Site Settings](#) — `GET`/`PATCH /api/v1/admin/settings`, whitelisted key `log_retention_days`, default `90`, `0` = keep forever).
|
||||
|
||||
- A daily scheduled task (`App\Shared\Logging\Message\PruneLogsMessage`, registered in `src/Schedule.php`, routed to `scheduler_default`) deletes logs older than `log_retention_days`.
|
||||
- Manual prune: `php bin/console app:prune-logs` (reads the same setting, deletes older-than-retention rows, prints the count).
|
||||
- Requires the scheduler worker: `php bin/console messenger:consume scheduler_default`.
|
||||
|
||||
@@ -677,5 +677,6 @@
|
||||
"675": "Community 675",
|
||||
"676": "Community 676",
|
||||
"677": "Community 677",
|
||||
"678": "Community 678"
|
||||
"678": "Community 678",
|
||||
"679": "Community 679"
|
||||
}
|
||||
|
||||
+98
-113
@@ -1,16 +1,16 @@
|
||||
# Graph Report - clinicpro (2026-07-01)
|
||||
|
||||
## Corpus Check
|
||||
- 668 files · ~472,125 words
|
||||
- 672 files · ~472,879 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 8432 nodes · 11619 edges · 679 communities (551 shown, 128 thin omitted)
|
||||
- Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 264 edges (avg confidence: 0.8)
|
||||
- 8457 nodes · 11652 edges · 680 communities (547 shown, 133 thin omitted)
|
||||
- Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 265 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Graph Freshness
|
||||
- Built from commit: `31201322`
|
||||
- Built from commit: `a31e8b43`
|
||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||
- Run `graphify update .` after code changes (no API cost).
|
||||
|
||||
@@ -679,6 +679,7 @@
|
||||
- [[_COMMUNITY_Community 676|Community 676]]
|
||||
- [[_COMMUNITY_Community 677|Community 677]]
|
||||
- [[_COMMUNITY_Community 678|Community 678]]
|
||||
- [[_COMMUNITY_Community 679|Community 679]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `BaseController` - 76 edges
|
||||
@@ -688,46 +689,46 @@
|
||||
5. `Clinic` - 50 edges
|
||||
6. `Doctor` - 48 edges
|
||||
7. `useAuthStore` - 43 edges
|
||||
8. `ApiResponse` - 40 edges
|
||||
8. `ApiResponse` - 41 edges
|
||||
9. `formatDate()` - 39 edges
|
||||
10. `formatRial()` - 36 edges
|
||||
10. `AdminApiController` - 37 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- `ServiceTariffModal()` --calls--> `formatNumber()` [EXTRACTED]
|
||||
assets/admin/components/ServiceTariffModal.tsx → assets/admin/lib/utils.ts
|
||||
- `PersianDatePicker()` --calls--> `formatDate()` [EXTRACTED]
|
||||
assets/admin/components/ui/PersianDatePicker.tsx → assets/admin/lib/utils.ts
|
||||
- `LogsPage()` --calls--> `formatDateTime()` [EXTRACTED]
|
||||
assets/admin/pages/LogsPage.tsx → assets/admin/lib/utils.ts
|
||||
- `SmsPage()` --calls--> `formatDateTime()` [EXTRACTED]
|
||||
assets/admin/pages/SmsPage.tsx → assets/admin/lib/utils.ts
|
||||
- `NewAppointmentModal()` --calls--> `useAuthStore` [EXTRACTED]
|
||||
assets/admin/pages/AppointmentsPage.tsx → assets/admin/stores/authStore.ts
|
||||
- `LogoUploadField()` --calls--> `useAuthStore` [EXTRACTED]
|
||||
assets/admin/pages/CategoriesPage.tsx → assets/admin/stores/authStore.ts
|
||||
|
||||
## Import Cycles
|
||||
- None detected.
|
||||
|
||||
## Communities (679 total, 128 thin omitted)
|
||||
## Communities (680 total, 133 thin omitted)
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.05
|
||||
Nodes (40): ALLOWED_ROLES, PrivateRoute(), PublicRoute(), RoleRoute(), queryClient, toastStyle, LogoUploadField(), TabActions() (+32 more)
|
||||
Nodes (37): ALLOWED_ROLES, PrivateRoute(), PublicRoute(), RoleRoute(), queryClient, toastStyle, AddForm, addSchema (+29 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.03
|
||||
Nodes (41): AddressData, AddrForm, addrSchema, AVATAR_COLORS, BookingMeta, CityOpt, DateOverrideData, DEFAULT_BOOKING_META (+33 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.06
|
||||
Nodes (26): get, api, ApiError, getToken(), refreshOnce(), request(), { refreshMock, logoutMock }, replaceMock (+18 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (26): get, PaymentConfig, PaymentGatewayInfo, api, ApiError, getToken(), refreshOnce(), request() (+18 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.05
|
||||
Nodes (50): ApiResponse, PaginatedResponse, formatDate(), ALL_STATUSES, AppointmentDetailPage(), STATUS_FILTERS, FILTERS, Breakdown (+42 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (80): ApiResponse, PaginatedResponse, formatDate(), ALL_STATUSES, AppointmentDetailPage(), STATUS_FILTERS, FILTERS, Breakdown (+72 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.05
|
||||
Nodes (11): DoctorServiceController, DoctorService, Doctor, DoctorServiceRepository, Collection, self, User, WeeklySchedule (+3 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (8): DoctorService, Doctor, DoctorServiceRepository, Collection, self, User, WeeklySchedule, ManagerRegistry
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.07
|
||||
@@ -754,28 +755,28 @@ Cohesion: 0.04
|
||||
Nodes (46): Clinic Address Management, Clinic API, DELETE `/api/v1/admin/clinic/{clinicUuid}/doctor/{doctorUuid}`, `DELETE /api/v1/clinic/{clinicUuid}/address/{addressUuid}`, Errors, Errors, Errors, Errors (+38 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.06
|
||||
Nodes (38): PaymentConfig, PaymentGatewayInfo, usePaymentConfig(), SmsPage(), STATUS_LOG_META, Tab, TAG_FILTER_OPTIONS, TAG_LABELS (+30 more)
|
||||
Cohesion: 0.33
|
||||
Nodes (4): BlogController, JsonResponse, Request, User
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.05
|
||||
Nodes (44): Clinic Doctor Invitation API, DELETE `/api/v1/admin/clinic/invitation/{invUuid}`, Errors, Errors, Errors, Errors, Errors, Errors (+36 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.07
|
||||
Nodes (37): FreeVisitPrice(), Pricing, cn(), formatDateTime(), formatRial(), iranMobileOptionalSchema, iranMobileSchema, isValidIranMobile() (+29 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (35): FreeVisitPrice(), Pricing, usePaymentConfig(), formatDateTime(), formatRial(), InsurancePricingPage(), LogsPage(), FinancialSummary (+27 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.15
|
||||
Nodes (8): AppointmentSettingsController, Holiday, HolidayRepository, JsonResponse, Request, User, Doctor, ManagerRegistry
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.09
|
||||
Nodes (13): PaymentController, MellatGateway, MockGateway, JsonResponse, Payment, PaymentGatewayInterface, Request, Response (+5 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (16): PaymentController, MellatGateway, MockGateway, SepGateway, JsonResponse, Payment, PaymentGatewayInterface, Request (+8 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.06
|
||||
Nodes (8): PatientSession, SmsWallet, AppointmentExpiryService, Appointment, Collection, PatientRecord, self, SessionService
|
||||
Cohesion: 0.07
|
||||
Nodes (7): PatientSession, SmsWallet, Appointment, Collection, PatientRecord, self, SessionService
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.05
|
||||
@@ -798,8 +799,8 @@ Cohesion: 0.05
|
||||
Nodes (37): formatNumber(), ClinicDetailPage(), AdminCharts, AdminDashboard(), AdminRecent, AdminStats, APPT_CLS, APPT_COLOR (+29 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.09
|
||||
Nodes (15): RatingController, Like, Rate, CommentListNPlusOneTest, LikeRepository, RateRepository, JsonResponse, Request (+7 more)
|
||||
Cohesion: 0.15
|
||||
Nodes (9): RatingController, Like, LikeRepository, JsonResponse, Request, User, Comment, ManagerRegistry (+1 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.05
|
||||
@@ -814,8 +815,8 @@ Cohesion: 0.05
|
||||
Nodes (39): Appointment API, Error Responses, Errors, Errors, Errors, Errors, Errors, Errors (+31 more)
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.24
|
||||
Nodes (4): InsuranceController, JsonResponse, Request, User
|
||||
Cohesion: 0.15
|
||||
Nodes (7): InsuranceController, EntityInsurancePricing, EntityInsurancePricingRepository, JsonResponse, Request, User, ManagerRegistry
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.05
|
||||
@@ -826,8 +827,8 @@ Cohesion: 0.09
|
||||
Nodes (4): Appointment, Doctor, self, User
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.10
|
||||
Nodes (21): Bulk import / export, DELETE `/api/v1/admin/city/{id}`, DELETE `/api/v1/admin/province/{id}`, Errors, Errors, GET `/api/v1/admin/provinces`, GET `/api/v1/categorys/{bundle}` *(Legacy)*, GET `/api/v1/provinces` (+13 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (35): Bulk import / export, DELETE `/api/v1/admin/city/{id}`, DELETE `/api/v1/admin/province/{id}`, Errors, Errors, Errors, Errors, GET `/api/v1/admin/cities` (+27 more)
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.08
|
||||
@@ -842,8 +843,8 @@ Cohesion: 0.06
|
||||
Nodes (32): 10. Modal / Dialog, 11. Toast Notifications, 12. Empty States & Loading, 13. Page Header (هر صفحه), 14. تکنولوژی Stack, 15. Responsive Breakpoints, 16. Dark Mode (اختیاری — فاز دوم), 17. نمونه رنگبندی صفحه داشبورد (+24 more)
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.04
|
||||
Nodes (40): CoverageRow, Draft, KIND, TenantInsurance, ServiceTariffModal(), TariffResponse, TariffRow, Contract (+32 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (20): CoverageRow, Draft, KIND, TenantInsurance, ServiceTariffModal(), TariffResponse, TariffRow, EMPTY_ITEMS (+12 more)
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.06
|
||||
@@ -874,8 +875,8 @@ Cohesion: 0.09
|
||||
Nodes (4): User, PasswordAuthenticatedUserInterface, self, UserInterface
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 0.06
|
||||
Nodes (21): CityForm, citySchema, ImportError, InsuranceForm, insuranceSchema, ProvinceForm, provinceSchema, ServiceForm (+13 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (29): Contract, InsuranceOption, KIND_LABEL, CityForm, citySchema, ImportError, InsuranceForm, insuranceSchema (+21 more)
|
||||
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.07
|
||||
@@ -922,8 +923,8 @@ Cohesion: 0.07
|
||||
Nodes (26): الزامات UI, باگفیکس صفحه نوبتها, باگ ۱ — کرش تقویم, باگ ۲ — روز هفته در DateNavigator, باگ ۳ — پیام «slot نیست», باگ ۴ — نوبت جدید: نام اجباری + find-or-create patient, باگ ۵ — patient_mobile نشان میدهد موبایل پزشک, باگ ۶ — نوبتهای رزرو شده در نمایش زمانبندی (+18 more)
|
||||
|
||||
### Community 53 - "Community 53"
|
||||
Cohesion: 0.08
|
||||
Nodes (15): AppLogRepository, CityRepository, ClaimItemRepository, InvoiceItemRepository, PreRegistrationRepository, SiteConfigRepository, ServiceEntityRepository, ManagerRegistry (+7 more)
|
||||
Cohesion: 0.09
|
||||
Nodes (12): AppLogRepository, ClaimItemRepository, PreRegistrationRepository, SiteConfigRepository, TaxRateHistoryRepository, ServiceEntityRepository, ManagerRegistry, ManagerRegistry (+4 more)
|
||||
|
||||
### Community 54 - "Community 54"
|
||||
Cohesion: 0.10
|
||||
@@ -942,8 +943,8 @@ Cohesion: 0.08
|
||||
Nodes (24): Blog API, DELETE `/api/v1/blog/{uuid}`, Errors, Errors, Errors, Errors, Errors, GET `/api/v1/blog/{slug}` (+16 more)
|
||||
|
||||
### Community 58 - "Community 58"
|
||||
Cohesion: 0.15
|
||||
Nodes (8): Blog, BlogController, BlogRepository, JsonResponse, Request, User, ManagerRegistry, QueryBuilder
|
||||
Cohesion: 0.28
|
||||
Nodes (4): Blog, BlogRepository, ManagerRegistry, QueryBuilder
|
||||
|
||||
### Community 59 - "Community 59"
|
||||
Cohesion: 0.22
|
||||
@@ -966,8 +967,8 @@ Cohesion: 0.22
|
||||
Nodes (5): AuthController, RateLimiterFactory, JsonResponse, Request, User
|
||||
|
||||
### Community 64 - "Community 64"
|
||||
Cohesion: 0.32
|
||||
Nodes (4): SmsWalletController, JsonResponse, Request, User
|
||||
Cohesion: 0.20
|
||||
Nodes (7): SmsWalletController, SmsSettingsRepository, SmsSettings, JsonResponse, Request, User, ManagerRegistry
|
||||
|
||||
### Community 65 - "Community 65"
|
||||
Cohesion: 0.08
|
||||
@@ -1010,8 +1011,8 @@ Cohesion: 0.16
|
||||
Nodes (5): DoctorSecretary, Clinic, Doctor, self, User
|
||||
|
||||
### Community 77 - "Community 77"
|
||||
Cohesion: 0.08
|
||||
Nodes (15): Claim, ClaimItem, DebtRow, InsuranceOption, KIND_LABEL, STATUS_FILTERS, STATUS_META, IbanItem (+7 more)
|
||||
Cohesion: 0.10
|
||||
Nodes (13): Claim, ClaimItem, DebtRow, InsuranceOption, KIND_LABEL, STATUS_FILTERS, STATUS_META, IbanItem (+5 more)
|
||||
|
||||
### Community 78 - "Community 78"
|
||||
Cohesion: 0.12
|
||||
@@ -1039,7 +1040,7 @@ Nodes (29): devDependencies, @babel/core, @babel/preset-env, @babel/preset-react
|
||||
|
||||
### Community 86 - "Community 86"
|
||||
Cohesion: 0.06
|
||||
Nodes (15): AppointmentExpiryServiceTest, LowTierFixesTest, SendCodeMobileRateLimitTest, ClaimsListPaginationTest, ServiceItemStaffOwnershipTest, EntityManagerInterface, KernelBrowser, CommentPaginationTest (+7 more)
|
||||
Nodes (15): AppointmentExpiryServiceTest, LowTierFixesTest, SendCodeMobileRateLimitTest, ClaimsListPaginationTest, ServiceItemDeleteCleanupTest, ServiceItemStaffOwnershipTest, EntityManagerInterface, KernelBrowser (+7 more)
|
||||
|
||||
### Community 87 - "Community 87"
|
||||
Cohesion: 0.10
|
||||
@@ -1126,8 +1127,8 @@ Cohesion: 0.32
|
||||
Nodes (6): AppointmentController, Appointment, Doctor, JsonResponse, Request, User
|
||||
|
||||
### Community 108 - "Community 108"
|
||||
Cohesion: 0.13
|
||||
Nodes (10): BaseController, CategoryController, CategoryImportController, SmsMessageController, JsonResponse, JsonResponse, Request, JsonResponse (+2 more)
|
||||
Cohesion: 0.12
|
||||
Nodes (12): AbstractController, AdminController, BaseController, CategoryController, CategoryImportController, HomeController, Response, JsonResponse (+4 more)
|
||||
|
||||
### Community 109 - "Community 109"
|
||||
Cohesion: 0.29
|
||||
@@ -1327,7 +1328,7 @@ Nodes (15): Date Overrides, DELETE `/api/v1/appointment-settings/date-override/{
|
||||
|
||||
### Community 161 - "Community 161"
|
||||
Cohesion: 0.24
|
||||
Nodes (7): ClaimSubmitterInterface, ClaimService, ManualClaimSubmitter, Claim, Invoice, Claim, ClaimSubmissionResult
|
||||
Nodes (5): ClaimsListNPlusOneTest, ClaimItem, ClaimService, Claim, Invoice
|
||||
|
||||
### Community 162 - "Community 162"
|
||||
Cohesion: 0.13
|
||||
@@ -1474,12 +1475,12 @@ Cohesion: 0.14
|
||||
Nodes (13): Endpoint ها, PATCH /api/v1/secretary/{uuid}, POST /api/v1/secretary, تسک ۱۴: ماژول منشی, توضیح, زمان تخمینی, ساختار JSON, سیستم مجوزها — Resource-Based Permissions (مقیاسپذیر) (+5 more)
|
||||
|
||||
### Community 201 - "Community 201"
|
||||
Cohesion: 0.15
|
||||
Nodes (13): Admin API, Clinic Invitation Management, GET `/api/v1/admin/representations`, GET `/api/v1/admin/secretaries`, GET /api/v1/admin/settings, PATCH /api/v1/admin/settings, Query Parameters, Query Parameters (+5 more)
|
||||
Cohesion: 0.20
|
||||
Nodes (10): Admin API, Clinic Invitation Management, GET `/api/v1/admin/comments`, GET `/api/v1/admin/rates`, GET /api/v1/admin/settings, PATCH /api/v1/admin/settings, Query Parameters, Query Parameters (+2 more)
|
||||
|
||||
### Community 202 - "Community 202"
|
||||
Cohesion: 0.15
|
||||
Nodes (13): DELETE `/api/v1/appointment-settings/holidays/{uuid}`, Errors, GET `/api/v1/appointment-settings/holidays/list/{doctorUuid}`, GET `/api/v1/appointment-settings/holidays/{uuid}`, Holidays, PATCH `/api/v1/appointment-settings/holidays/{uuid}`, POST `/api/v1/appointment-settings/holidays`, Request Body (+5 more)
|
||||
Cohesion: 0.10
|
||||
Nodes (19): Appointment Settings API, Available Locations, DELETE `/api/v1/appointment-settings/holidays/{uuid}`, Errors, Errors, `GET /api/v1/appointment-settings/available-locations/{doctorUuid}`, GET `/api/v1/appointment-settings/holidays/list/{doctorUuid}`, GET `/api/v1/appointment-settings/holidays/{uuid}` (+11 more)
|
||||
|
||||
### Community 203 - "Community 203"
|
||||
Cohesion: 0.15
|
||||
@@ -1490,8 +1491,8 @@ Cohesion: 0.21
|
||||
Nodes (6): AuthenticationException, ExceptionSubscriber, SecurityHeadersSubscriber, EventSubscriberInterface, ExceptionEvent, ResponseEvent
|
||||
|
||||
### Community 205 - "Community 205"
|
||||
Cohesion: 0.13
|
||||
Nodes (13): Command, CancelExpiredAppointmentsCommand, CreateAdminCommand, SeedCategoriesCommand, SeedSmsMessageTemplatesCommand, InputInterface, OutputInterface, InputInterface (+5 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (20): Command, CancelExpiredAppointmentsCommand, CreateAdminCommand, PruneLogsCommand, SeedCategoriesCommand, SeedSmsMessageTemplatesCommand, SmsMessageTemplateRepository, SmsTextResolver (+12 more)
|
||||
|
||||
### Community 206 - "Community 206"
|
||||
Cohesion: 0.35
|
||||
@@ -1566,8 +1567,8 @@ Cohesion: 0.30
|
||||
Nodes (6): AbstractAuthenticator, Passport, PasswordAuthenticator, Request, Response, TokenInterface
|
||||
|
||||
### Community 228 - "Community 228"
|
||||
Cohesion: 0.05
|
||||
Nodes (43): Bulk import / export, DELETE `/api/v1/admin/insurance/{id}`, DELETE `/api/v1/billing/tenant-insurances/{uuid}`, DELETE `/api/v1/insurance/{id}`, EntityInsurancePricing — قیمتگذاری ویزیت بر اساس بیمه, Errors, Errors, Errors (+35 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (47): Bulk import / export, DELETE `/api/v1/admin/insurance/{id}`, DELETE `/api/v1/billing/tenant-insurances/{uuid}`, DELETE `/api/v1/insurance/{id}`, EntityInsurancePricing — قیمتگذاری ویزیت بر اساس بیمه, Errors, Errors, Errors (+39 more)
|
||||
|
||||
### Community 229 - "Community 229"
|
||||
Cohesion: 0.15
|
||||
@@ -1674,8 +1675,8 @@ Cohesion: 0.29
|
||||
Nodes (3): FinancialBreakdown, Payment, User
|
||||
|
||||
### Community 258 - "Community 258"
|
||||
Cohesion: 0.22
|
||||
Nodes (5): ExpireAppointmentsMessage, ExpireAppointmentsHandler, ScheduleProviderInterface, Schedule, SymfonySchedule
|
||||
Cohesion: 0.15
|
||||
Nodes (7): ExpireAppointmentsMessage, ExpireAppointmentsHandler, PruneLogsHandler, PruneLogsMessage, ScheduleProviderInterface, Schedule, SymfonySchedule
|
||||
|
||||
### Community 259 - "Community 259"
|
||||
Cohesion: 0.18
|
||||
@@ -1702,8 +1703,8 @@ Cohesion: 0.33
|
||||
Nodes (4): RepresentationRepository, ManagerRegistry, Representation, User
|
||||
|
||||
### Community 265 - "Community 265"
|
||||
Cohesion: 0.15
|
||||
Nodes (17): calcFinalPrice(), EMPTY_RECORDS, EMPTY_SESSIONS, fileNumber(), getPatientName(), getPatientPhone(), InsurancePricing, MyPatientsPageInner() (+9 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (27): calcFinalPrice(), EMPTY_RECORDS, EMPTY_SESSIONS, fileNumber(), getPatientName(), getPatientPhone(), InsurancePricing, MyPatientsPageInner() (+19 more)
|
||||
|
||||
### Community 266 - "Community 266"
|
||||
Cohesion: 0.33
|
||||
@@ -1738,7 +1739,7 @@ Cohesion: 0.18
|
||||
Nodes (10): Endpoint های موجود که تغییر میکنند, GET /api/v1/admin/dashboard/charts?from=UNIX&to=UNIX, GET /api/v1/dashboard/clinic, GET /api/v1/dashboard/doctor, تسک ۱۶: داشبورد هوشمند — چارت + فیلتر زمانی, توضیح, زمان تخمینی, فیلتر بازه زمانی (+2 more)
|
||||
|
||||
### Community 274 - "Community 274"
|
||||
Cohesion: 0.21
|
||||
Cohesion: 0.20
|
||||
Nodes (5): Authentication, ClinicPro — API Documentation Index, Error Code Reference, Modules, Standard Response Envelope
|
||||
|
||||
### Community 275 - "Community 275"
|
||||
@@ -1818,8 +1819,8 @@ Cohesion: 0.24
|
||||
Nodes (10): gridItemStyle, JALALI_MONTHS, jalaliFirstWeekday(), jalaliToGregorian(), navBtnStyle, PersianCalendar(), pf, Props (+2 more)
|
||||
|
||||
### Community 295 - "Community 295"
|
||||
Cohesion: 0.22
|
||||
Nodes (8): AbstractController, AdminController, HomeController, SeoController, Response, Response, Request, Response
|
||||
Cohesion: 0.57
|
||||
Nodes (3): SeoController, Request, Response
|
||||
|
||||
### Community 296 - "Community 296"
|
||||
Cohesion: 0.22
|
||||
@@ -1855,7 +1856,7 @@ Nodes (16): آمادهسازی پروژه ClinicPro برای دیپلوی ر
|
||||
|
||||
### Community 304 - "Community 304"
|
||||
Cohesion: 0.22
|
||||
Nodes (9): 29. 🟢 `GET` clinic list 🆕, 30. 🟢 `GET` get 🆕, 33. 🔵 `POST` image_clinic, 6. کلینیک (Clinic), هدرهای اضافی, پارامترهای Query, پاسخها, پاسخها (+1 more)
|
||||
Nodes (9): 29. 🟢 `GET` clinic list 🆕, 30. 🟢 `GET` get 🆕, 36. 🟢 `GET` get my rate, 6. کلینیک (Clinic), هدرهای اضافی, پارامترهای Query, پاسخها, پاسخها (+1 more)
|
||||
|
||||
### Community 306 - "Community 306"
|
||||
Cohesion: 0.07
|
||||
@@ -2022,8 +2023,8 @@ Cohesion: 0.50
|
||||
Nodes (4): UserActiveContextRepository, ManagerRegistry, User, UserActiveContext
|
||||
|
||||
### Community 352 - "Community 352"
|
||||
Cohesion: 0.25
|
||||
Nodes (4): SmsMessageTemplateRepository, SmsTextResolver, SmsMessageTemplate, ManagerRegistry
|
||||
Cohesion: 0.36
|
||||
Nodes (3): DoctorServiceController, JsonResponse, Request
|
||||
|
||||
### Community 354 - "Community 354"
|
||||
Cohesion: 0.36
|
||||
@@ -2186,8 +2187,8 @@ Cohesion: 0.33
|
||||
Nodes (6): Refactoring Plan, فاز ۰ — مستندسازی (۳ تا ۵ روز، قبل از هر کدنویسی), فاز ۱ — زیرساخت پایه (task-01), فاز ۲ — پیادهسازی ماژولها (به ترتیب dependency), فاز ۳ — بهینهسازی (بعد از پیادهسازی), فاز ۴ — آمادهسازی تولید
|
||||
|
||||
### Community 397 - "Community 397"
|
||||
Cohesion: 0.21
|
||||
Nodes (6): ClaimAmountBoundsTest, ClaimsListNPlusOneTest, ClaimItem, Claim, Doctor, User
|
||||
Cohesion: 0.36
|
||||
Nodes (4): ClaimAmountBoundsTest, Claim, Doctor, User
|
||||
|
||||
### Community 398 - "Community 398"
|
||||
Cohesion: 0.29
|
||||
@@ -2214,20 +2215,20 @@ Cohesion: 0.11
|
||||
Nodes (18): [F10] راهنمای کهنه در `CLAUDE.md`: endpoint `categorys/{bundle}` منتقل شده, [F11] داشبورد دکتر `GET /api/v1/dashboard/doctor` همیشه 500 (فیلد ناموجود در DQL) — ✅ رفع شد, [F1] phpstan: مقایسهٔ همیشهدرست در محاسبهٔ estimated SMS — ✅ رفع شد, [F2] تستهای PHPUnit به API خارجی Kavenegar درخواست واقعی میزنند, [F3] دیتابیس تست seed نشده — فقط کاربر ادمین وجود دارد, [F4] اسکریپت seeder `create_test_users.php` وجود ندارد, [F5] ادمین با JWT معتبر به `/api/doc` (Swagger UI) دسترسی ندارد (401), [F6] ناسازگاری کدهای خطا بین دامنهها (+10 more)
|
||||
|
||||
### Community 452 - "Community 452"
|
||||
Cohesion: 0.08
|
||||
Nodes (16): ClinicAddress, ClinicDoctorItem, ClinicInvitation, EditForm, editSchema, HUES_LIST, INV_STATUS_MAP, IRAN_CENTER (+8 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (34): cn(), iranMobileOptionalSchema, iranMobileSchema, isValidIranMobile(), maskMobile(), sanitizeMobileInput(), toDate(), toEnglishDigits() (+26 more)
|
||||
|
||||
### Community 456 - "Community 456"
|
||||
Cohesion: 0.29
|
||||
Nodes (6): Appointment Settings API, Available Locations, Errors, `GET /api/v1/appointment-settings/available-locations/{doctorUuid}`, Response `200`, Slot Calculation Logic (Reference)
|
||||
Cohesion: 0.38
|
||||
Nodes (5): Rate, RateRepository, Doctor, ManagerRegistry, User
|
||||
|
||||
### Community 458 - "Community 458"
|
||||
Cohesion: 0.47
|
||||
Nodes (6): formatPersianDate(), gToJ(), jFirstDayOfWeek(), PersianDateInput(), todayGregorian(), toPersianNums()
|
||||
|
||||
### Community 459 - "Community 459"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): API موجود (نیاز به تغییر ندارند), اپیک ۳ — منشی (Secretary) — تکمیل, تغییرات مورد نیاز, توضیح, نیازمندیهای کارکردی
|
||||
Cohesion: 0.33
|
||||
Nodes (6): API موجود (نیاز به تغییر ندارند), اپیکها, اپیک ۳ — منشی (Secretary) — تکمیل, تغییرات مورد نیاز, توضیح, نیازمندیهای کارکردی
|
||||
|
||||
### Community 460 - "Community 460"
|
||||
Cohesion: 0.33
|
||||
@@ -2294,8 +2295,8 @@ Cohesion: 0.33
|
||||
Nodes (5): ایندکسها, جدول: sms_accounts (حساب پیامک), جدول: sms_queue (صف پیامک), نکات مهم, پایگاه داده — تسک ۱۷: ماژول پیامک
|
||||
|
||||
### Community 478 - "Community 478"
|
||||
Cohesion: 0.40
|
||||
Nodes (5): GET `/api/v1/admin/comments`, GET `/api/v1/admin/rates`, Query Parameters, Query Parameters, Rating & Comment Management
|
||||
Cohesion: 0.39
|
||||
Nodes (3): CityRepository, City, ManagerRegistry
|
||||
|
||||
### Community 479 - "Community 479"
|
||||
Cohesion: 0.40
|
||||
@@ -2330,8 +2331,8 @@ Cohesion: 0.40
|
||||
Nodes (5): Admin Endpoints, GET /api/v1/admin/sms/settings/review, GET /api/v1/admin/sms/wallet-report, POST /api/v1/admin/sms/settings/{id}/approve, POST /api/v1/admin/sms/settings/{id}/reject
|
||||
|
||||
### Community 491 - "Community 491"
|
||||
Cohesion: 0.15
|
||||
Nodes (5): EntityInsurancePricing, TenantInsuranceCleanupTest, EntityInsurancePricingRepository, TenantInsuranceCleanupService, ManagerRegistry
|
||||
Cohesion: 0.43
|
||||
Nodes (3): SmsMessageController, JsonResponse, Request
|
||||
|
||||
### Community 493 - "Community 493"
|
||||
Cohesion: 0.20
|
||||
@@ -2362,8 +2363,8 @@ Cohesion: 0.40
|
||||
Nodes (5): addMinutes(), calcSlotCount(), hasOverlap(), parseMinutes(), SessionEditor()
|
||||
|
||||
### Community 500 - "Community 500"
|
||||
Cohesion: 0.33
|
||||
Nodes (6): API موجود (نیاز به endpoint جدید ندارد), اپیکها, اپیک ۷ — داشبورد هوشمند (Smart Dashboard), تغییر مورد نیاز, توضیح, نیازمندیهای کارکردی
|
||||
Cohesion: 0.40
|
||||
Nodes (5): API موجود (نیاز به endpoint جدید ندارد), اپیک ۷ — داشبورد هوشمند (Smart Dashboard), تغییر مورد نیاز, توضیح, نیازمندیهای کارکردی
|
||||
|
||||
### Community 501 - "Community 501"
|
||||
Cohesion: 0.40
|
||||
@@ -2502,8 +2503,8 @@ Cohesion: 0.67
|
||||
Nodes (3): Errors, GET `/api/v1/notification-mobile/{target}`, Response `200`
|
||||
|
||||
### Community 540 - "Community 540"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, POST `/api/v1/admin/insurance`, Request Body (`application/json`), Response `201`
|
||||
Cohesion: 0.43
|
||||
Nodes (3): InvoiceItemRepository, InvoiceItem, ManagerRegistry
|
||||
|
||||
### Community 542 - "Community 542"
|
||||
Cohesion: 0.15
|
||||
@@ -2666,8 +2667,8 @@ Cohesion: 0.20
|
||||
Nodes (10): Configuration, DELETE `/api/v1/sms/template/{uuid}`, Errors, Errors, GET `/api/v1/admin/sms/templates`, GET `/api/v1/sms/template/{uuid}`, Response `200`, Response `200` (+2 more)
|
||||
|
||||
### Community 598 - "Community 598"
|
||||
Cohesion: 0.31
|
||||
Nodes (3): SepGateway, PaymentInitResult, PaymentVerifyResult
|
||||
Cohesion: 0.53
|
||||
Nodes (4): ClaimSubmitterInterface, ManualClaimSubmitter, Claim, ClaimSubmissionResult
|
||||
|
||||
### Community 599 - "Community 599"
|
||||
Cohesion: 0.67
|
||||
@@ -2706,8 +2707,8 @@ Cohesion: 0.67
|
||||
Nodes (3): بکاند, فرانتاند, وضعیت فعلی کد (مهم — قبل از تغییر بخوان)
|
||||
|
||||
### Community 618 - "Community 618"
|
||||
Cohesion: 0.16
|
||||
Nodes (5): ServiceItemDeleteCleanupTest, ServiceCoverageNPlusOneTest, TenantServiceCoverageRepository, ManagerRegistry, TenantServiceCoverage
|
||||
Cohesion: 0.12
|
||||
Nodes (6): ServiceCoverageNPlusOneTest, TenantInsuranceCleanupTest, TenantServiceCoverageRepository, TenantInsuranceCleanupService, ManagerRegistry, TenantServiceCoverage
|
||||
|
||||
### Community 631 - "Community 631"
|
||||
Cohesion: 0.50
|
||||
@@ -2729,10 +2730,6 @@ Nodes (8): ۲.۲ انواع دستهبندی (Category Types), ۲.۲.۱ تگ
|
||||
Cohesion: 0.43
|
||||
Nodes (5): BeforeInstallPromptEvent, usePwaInstall(), PwaInstallBanner(), detectIOS(), PwaLoginCard()
|
||||
|
||||
### Community 636 - "Community 636"
|
||||
Cohesion: 0.48
|
||||
Nodes (3): SmsSettingsRepository, SmsSettings, ManagerRegistry
|
||||
|
||||
### Community 638 - "Community 638"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): GET `/api/v1/clinic-pro/doctor-addresses/{doctorId}`, Path Parameters, Response `200`
|
||||
@@ -2757,10 +2754,6 @@ Nodes (5): 31. 🟡 `PATCH` patch, Request Body, مثال Request, هدرهای
|
||||
Cohesion: 0.40
|
||||
Nodes (5): 32. 🔵 `POST` post, Request Body, مثال Request, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 651 - "Community 651"
|
||||
Cohesion: 0.53
|
||||
Nodes (3): TaxRateHistoryRepository, ManagerRegistry, TaxRateHistory
|
||||
|
||||
### Community 652 - "Community 652"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): GET `/api/v1/admin/pre-registrations`, POST `/api/v1/admin/pre-registrations/{uuid}/approve`, POST `/api/v1/admin/pre-registrations/{uuid}/reject`, Pre-Registration Management
|
||||
@@ -2787,11 +2780,11 @@ Nodes (4): GET `/api/v1/admin/payments`, Payment Management, Query Parameters, R
|
||||
|
||||
### Community 658 - "Community 658"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, PATCH `/api/v1/admin/province/{id}`, Path Parameters, Response `200`
|
||||
Nodes (4): GET `/api/v1/admin/representations`, Query Parameters, Representation Management, Response `200`
|
||||
|
||||
### Community 659 - "Community 659"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Errors, POST `/api/v1/admin/city`, Request Body (`application/json`), Response `201`
|
||||
Nodes (4): GET `/api/v1/admin/secretaries`, Query Parameters, Response `200`, Secretary Management
|
||||
|
||||
### Community 660 - "Community 660"
|
||||
Cohesion: 0.50
|
||||
@@ -2802,8 +2795,8 @@ Cohesion: 0.50
|
||||
Nodes (4): 40. 🔵 `POST` post, مثال Request, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 662 - "Community 662"
|
||||
Cohesion: 0.50
|
||||
Nodes (4): Application Logs, GET `/api/v1/admin/logs`, Query Parameters, Response `200`
|
||||
Cohesion: 0.29
|
||||
Nodes (7): Application Logs, DELETE `/api/v1/admin/logs`, GET `/api/v1/admin/logs`, Log Retention, Query Parameters, Response `200`, Response `200`
|
||||
|
||||
### Community 664 - "Community 664"
|
||||
Cohesion: 0.50
|
||||
@@ -2829,21 +2822,13 @@ Nodes (4): ۲.۱۳ نظرات، لایک و امتیازدهی, ۲.۱۳.۱ نظ
|
||||
Cohesion: 0.50
|
||||
Nodes (4): GET `/api/v1/admin/settlements`, Query Parameters, Response `200`, Settlement Management
|
||||
|
||||
### Community 673 - "Community 673"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): GET `/api/v1/admin/cities`, Query Parameters, Response `200`
|
||||
|
||||
### Community 674 - "Community 674"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): GET `/api/v1/cities`, Query Parameters, Response `200`
|
||||
|
||||
### Community 675 - "Community 675"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): 34. 🔵 `POST` image logo, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 676 - "Community 676"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): 36. 🟢 `GET` get my rate, هدرهای اضافی, پاسخها
|
||||
Nodes (3): 33. 🔵 `POST` image_clinic, هدرهای اضافی, پاسخها
|
||||
|
||||
### Community 677 - "Community 677"
|
||||
Cohesion: 0.67
|
||||
@@ -2854,24 +2839,24 @@ Cohesion: 0.67
|
||||
Nodes (3): 43. 🟢 `GET` get, هدرهای اضافی, پاسخها
|
||||
|
||||
## Knowledge Gaps
|
||||
- **3640 isolated node(s):** `ALLOWED_ROLES`, `Pricing`, `TenantInsurance`, `CoverageRow`, `Draft` (+3635 more)
|
||||
- **3644 isolated node(s):** `ALLOWED_ROLES`, `Pricing`, `TenantInsurance`, `CoverageRow`, `Draft` (+3639 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **128 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
- **133 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `BaseController` connect `Community 108` to `Community 4`, `Community 6`, `Community 138`, `Community 139`, `Community 14`, `Community 15`, `Community 275`, `Community 20`, `Community 22`, `Community 26`, `Community 164`, `Community 295`, `Community 300`, `Community 301`, `Community 175`, `Community 176`, `Community 177`, `Community 58`, `Community 59`, `Community 63`, `Community 64`, `Community 70`, `Community 71`, `Community 75`, `Community 206`, `Community 230`, `Community 103`, `Community 104`, `Community 107`, `Community 109`, `Community 121`, `Community 122`, `Community 252`?**
|
||||
_High betweenness centrality (0.030) - this node is a cross-community bridge._
|
||||
- **Why does `BaseController` connect `Community 108` to `Community 6`, `Community 138`, `Community 11`, `Community 139`, `Community 14`, `Community 15`, `Community 275`, `Community 20`, `Community 22`, `Community 26`, `Community 164`, `Community 300`, `Community 301`, `Community 175`, `Community 176`, `Community 177`, `Community 59`, `Community 63`, `Community 64`, `Community 70`, `Community 71`, `Community 75`, `Community 206`, `Community 352`, `Community 230`, `Community 103`, `Community 104`, `Community 107`, `Community 491`, `Community 109`, `Community 121`, `Community 122`, `Community 252`?**
|
||||
_High betweenness centrality (0.031) - this node is a cross-community bridge._
|
||||
- **Why does `AppointmentRepository` connect `Community 119` to `Community 53`?**
|
||||
_High betweenness centrality (0.017) - this node is a cross-community bridge._
|
||||
_High betweenness centrality (0.021) - this node is a cross-community bridge._
|
||||
- **Why does `Version20260614181657` connect `Community 431` to `Community 399`?**
|
||||
_High betweenness centrality (0.015) - this node is a cross-community bridge._
|
||||
- **What connects `ALLOWED_ROLES`, `Pricing`, `TenantInsurance` to the rest of the system?**
|
||||
_3640 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
_3644 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
- **Should `Community 0` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.05012531328320802 - nodes in this community are weakly interconnected._
|
||||
_Cohesion score 0.054426705370101594 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 1` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.028985507246376812 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 2` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.06280193236714976 - nodes in this community are weakly interconnected._
|
||||
_Cohesion score 0.06565656565656566 - nodes in this community are weakly interconnected._
|
||||
graphify-out/cache/ast/v0.8.44/1ea5a9d3ace2fce4b476ccb2b0954b06f3d4c364fa9e7aa9d77c25a8a6631d1c.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/20acb36c09cd00f50865c9d8bd04f8234eb0a76285a744e34d5cd2e50e3f4eaf.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_messagehandler_prunelogshandler_php", "label": "PruneLogsHandler.php", "file_type": "code", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L1"}, {"id": "messagehandler_prunelogshandler_prunelogshandler", "label": "PruneLogsHandler", "file_type": "code", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L9"}, {"id": "messagehandler_prunelogshandler_prunelogshandler_construct", "label": ".__construct()", "file_type": "code", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L12"}, {"id": "messagehandler_prunelogshandler_prunelogshandler_invoke", "label": ".__invoke()", "file_type": "code", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L14"}, {"id": "prunelogsmessage", "label": "PruneLogsMessage", "file_type": "code", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L14"}], "edges": [{"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_messagehandler_prunelogshandler_php", "target": "logpruneservice", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L5", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_messagehandler_prunelogshandler_php", "target": "prunelogsmessage", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L6", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_messagehandler_prunelogshandler_php", "target": "asmessagehandler", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L7", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_messagehandler_prunelogshandler_php", "target": "messagehandler_prunelogshandler_prunelogshandler", "relation": "contains", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L9", "weight": 1.0}, {"source": "messagehandler_prunelogshandler_prunelogshandler", "target": "messagehandler_prunelogshandler_prunelogshandler_construct", "relation": "method", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L12", "weight": 1.0}, {"source": "messagehandler_prunelogshandler_prunelogshandler", "target": "messagehandler_prunelogshandler_prunelogshandler_invoke", "relation": "method", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L14", "weight": 1.0}, {"source": "messagehandler_prunelogshandler_prunelogshandler_invoke", "target": "prunelogsmessage", "relation": "references", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L14", "weight": 1.0, "context": "parameter_type"}], "raw_calls": [{"caller_nid": "messagehandler_prunelogshandler_prunelogshandler_invoke", "callee": "pruneByRetention", "is_member_call": true, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/src/Shared/Logging/MessageHandler/PruneLogsHandler.php", "source_location": "L16", "receiver": null}]}
|
||||
graphify-out/cache/ast/v0.8.44/585dcd897dd9a9fa7067c31f37af6fa02033b436c3560680eed226e794f4b019.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/5ea072c77a0f25c8ccdd23616656fce0b88b93408c6cd461d98e9b18a6fa85b2.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/85a187db275df122f2800fd1e33608a0d63dbe3b394fcbbdbddcfc3a7fe565fa.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/89b686229a14b7fb90c315c5de4c70829250a27555083f77dcd18aab8b6b56df.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/9fe084116ccdb7a551ccae4c8d48626f7370549ae386e31c112cb579d8647ba6.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_logpruneservice_php", "label": "LogPruneService.php", "file_type": "code", "source_file": "src/Shared/Logging/LogPruneService.php", "source_location": "L1"}, {"id": "logging_logpruneservice_logpruneservice", "label": "LogPruneService", "file_type": "code", "source_file": "src/Shared/Logging/LogPruneService.php", "source_location": "L11"}, {"id": "logging_logpruneservice_logpruneservice_construct", "label": ".__construct()", "file_type": "code", "source_file": "src/Shared/Logging/LogPruneService.php", "source_location": "L13"}, {"id": "logging_logpruneservice_logpruneservice_prunebyretention", "label": ".pruneByRetention()", "file_type": "code", "source_file": "src/Shared/Logging/LogPruneService.php", "source_location": "L19"}], "edges": [{"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_logpruneservice_php", "target": "siteconfigrepository", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/LogPruneService.php", "source_location": "L5", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_logpruneservice_php", "target": "logging_logpruneservice_logpruneservice", "relation": "contains", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/LogPruneService.php", "source_location": "L11", "weight": 1.0}, {"source": "logging_logpruneservice_logpruneservice", "target": "logging_logpruneservice_logpruneservice_construct", "relation": "method", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/LogPruneService.php", "source_location": "L13", "weight": 1.0}, {"source": "logging_logpruneservice_logpruneservice", "target": "logging_logpruneservice_logpruneservice_prunebyretention", "relation": "method", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/LogPruneService.php", "source_location": "L19", "weight": 1.0}], "raw_calls": [{"caller_nid": "logging_logpruneservice_logpruneservice_prunebyretention", "callee": "get", "is_member_call": true, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/src/Shared/Logging/LogPruneService.php", "source_location": "L21", "receiver": null}, {"caller_nid": "logging_logpruneservice_logpruneservice_prunebyretention", "callee": "time", "is_member_call": false, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/src/Shared/Logging/LogPruneService.php", "source_location": "L26", "receiver": null}, {"caller_nid": "logging_logpruneservice_logpruneservice_prunebyretention", "callee": "deleteOlderThan", "is_member_call": true, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/src/Shared/Logging/LogPruneService.php", "source_location": "L28", "receiver": null}]}
|
||||
graphify-out/cache/ast/v0.8.44/d060dc486c8bcd48d507d001c65b68f4e3b84eb5cdccede2272bcfd2437ca142.json
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_message_prunelogsmessage_php", "label": "PruneLogsMessage.php", "file_type": "code", "source_file": "src/Shared/Logging/Message/PruneLogsMessage.php", "source_location": "L1"}, {"id": "message_prunelogsmessage_prunelogsmessage", "label": "PruneLogsMessage", "file_type": "code", "source_file": "src/Shared/Logging/Message/PruneLogsMessage.php", "source_location": "L5"}], "edges": [{"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_src_shared_logging_message_prunelogsmessage_php", "target": "message_prunelogsmessage_prunelogsmessage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "src/Shared/Logging/Message/PruneLogsMessage.php", "source_location": "L5", "weight": 1.0}], "raw_calls": []}
|
||||
graphify-out/cache/ast/v0.8.44/e1ed1b5ef5b6100bcb8a1e0d3e361ce40a2721501cdb5f14aa6fba8739654eee.json
Vendored
+1
File diff suppressed because one or more lines are too long
graphify-out/cache/ast/v0.8.44/e80ac76c51bb7ac78fe69679cbe82454f65f5ba6b35d3b3c3a01260698c35c6d.json
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1231
-638
File diff suppressed because it is too large
Load Diff
+37
-17
@@ -810,8 +810,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Admin/Controller/AdminApiController.php": {
|
||||
"mtime": 1782750165.3401215,
|
||||
"ast_hash": "09658d49a5db4c963fdbb7a167c8a38f",
|
||||
"mtime": 1782929751.8803875,
|
||||
"ast_hash": "8e5c78db08130ce9f65763e3306cc291",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Admin/Controller/AdminController.php": {
|
||||
@@ -1150,8 +1150,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Config/Controller/SiteConfigController.php": {
|
||||
"mtime": 1782893916.7348526,
|
||||
"ast_hash": "cb994abce239f65aa6ea56e240beaaf9",
|
||||
"mtime": 1782929760.8594074,
|
||||
"ast_hash": "1a0cbe1845407beebacb2eba7d07024a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Config/Entity/SiteConfig.php": {
|
||||
@@ -1165,8 +1165,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Config/Repository/SiteConfigRepository.php": {
|
||||
"mtime": 1782893889.148054,
|
||||
"ast_hash": "fafe887c5ba8de7a9185969f028a79d9",
|
||||
"mtime": 1782929759.6605961,
|
||||
"ast_hash": "d86906f3319b64f8ac975f31f438cac2",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Config/Repository/TaxRateHistoryRepository.php": {
|
||||
@@ -1470,12 +1470,12 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Schedule.php": {
|
||||
"mtime": 1781551622.4124491,
|
||||
"ast_hash": "e2679b3623e4a90ba9997ba7d8f9cda1",
|
||||
"mtime": 1782929801.2075908,
|
||||
"ast_hash": "af316acd9e95a609a79930f784f9b0d7",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Secretary/Controller/SecretaryController.php": {
|
||||
"mtime": 1782902851.0718935,
|
||||
"mtime": 1782929066.3195713,
|
||||
"ast_hash": "9cbf2e62e58b071b044dda089612dd4a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
@@ -2230,8 +2230,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"config/packages/messenger.yaml": {
|
||||
"mtime": 1781551622.4095502,
|
||||
"ast_hash": "44c3176965808e27425e9db57027b29b",
|
||||
"mtime": 1782929813.4472997,
|
||||
"ast_hash": "2bcd86d1b6bfe2890abcb27a71bb7c65",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"config/packages/nelmio_api_doc.yaml": {
|
||||
@@ -2340,8 +2340,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"docs/api/admin.md": {
|
||||
"mtime": 1782750435.7712955,
|
||||
"ast_hash": "6a68974b5f71e8d4104ffea4cab99edf",
|
||||
"mtime": 1782930000.5470378,
|
||||
"ast_hash": "27318238aa2741292c79a491870ba31b",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"docs/api/appointment-settings.md": {
|
||||
@@ -3375,8 +3375,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"assets/admin/pages/LogsPage.tsx": {
|
||||
"mtime": 1782750290.0145962,
|
||||
"ast_hash": "76db47af844faf3459083afa2ded8aaf",
|
||||
"mtime": 1782929861.6704445,
|
||||
"ast_hash": "2e9aed8e9f683011985c6c932644892a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"migrations/Version20260629161536.php": {
|
||||
@@ -3390,8 +3390,8 @@
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Shared/Logging/AppLogRepository.php": {
|
||||
"mtime": 1782749721.788873,
|
||||
"ast_hash": "b0bba22cca2483a041b2c719812d7729",
|
||||
"mtime": 1782929739.4106045,
|
||||
"ast_hash": "3cbcf00989521e8b47167454984b7314",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Shared/Logging/DbLogger.php": {
|
||||
@@ -3483,5 +3483,25 @@
|
||||
"mtime": 1782896060.8114219,
|
||||
"ast_hash": "9d1436d8787330a1cb06c54c4e896069",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Shared/Logging/Command/PruneLogsCommand.php": {
|
||||
"mtime": 1782929791.8492818,
|
||||
"ast_hash": "7ca54a0ec12d6d65f31ca365023eced1",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Shared/Logging/LogPruneService.php": {
|
||||
"mtime": 1782929784.9897113,
|
||||
"ast_hash": "42fa8560fd10cfdfc42203baf01d4406",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Shared/Logging/Message/PruneLogsMessage.php": {
|
||||
"mtime": 1782929786.004423,
|
||||
"ast_hash": "88c8235f5fd97e0410fe8dd634c59f7a",
|
||||
"semantic_hash": ""
|
||||
},
|
||||
"src/Shared/Logging/MessageHandler/PruneLogsHandler.php": {
|
||||
"mtime": 1782929788.2918966,
|
||||
"ast_hash": "7d306f9d2eba4693cd01ff21f2eda173",
|
||||
"semantic_hash": ""
|
||||
}
|
||||
}
|
||||
@@ -1989,4 +1989,12 @@ class AdminApiController extends BaseController
|
||||
'created_at' => (int) $l['createdAt'],
|
||||
], $rows), (int) $total, $page, $limit);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/admin/logs', methods: ['DELETE'])]
|
||||
public function clearLogs(): JsonResponse
|
||||
{
|
||||
$deleted = $this->em->getRepository(AppLog::class)->deleteAll();
|
||||
|
||||
return $this->success(['deleted' => $deleted]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ class SiteConfigController extends BaseController
|
||||
'support_phone',
|
||||
'max_cancel_hours_before',
|
||||
'appointment_reminder_hours',
|
||||
'log_retention_days',
|
||||
// payment gateways
|
||||
'payment_test_mode',
|
||||
'payment_allowed_frontend_hosts',
|
||||
|
||||
@@ -22,6 +22,8 @@ class SiteConfigRepository extends ServiceEntityRepository
|
||||
'support_phone' => '',
|
||||
'max_cancel_hours_before' => '24',
|
||||
'appointment_reminder_hours' => '2',
|
||||
// logging (0 = نگهداری نامحدود)
|
||||
'log_retention_days' => '90',
|
||||
// payment gateways
|
||||
'payment_test_mode' => '0',
|
||||
'mellat_terminal_id' => '',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App;
|
||||
|
||||
use App\Appointment\Message\ExpireAppointmentsMessage;
|
||||
use App\Shared\Logging\Message\PruneLogsMessage;
|
||||
use Symfony\Component\Scheduler\Attribute\AsSchedule;
|
||||
use Symfony\Component\Scheduler\RecurringMessage;
|
||||
use Symfony\Component\Scheduler\Schedule as SymfonySchedule;
|
||||
@@ -24,6 +25,9 @@ class Schedule implements ScheduleProviderInterface
|
||||
->processOnlyLastMissedRun(true) // ensure only last missed task is run
|
||||
->add(
|
||||
RecurringMessage::every('1 minute', new ExpireAppointmentsMessage())
|
||||
)
|
||||
->add(
|
||||
RecurringMessage::every('1 day', new PruneLogsMessage())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,24 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
class AppLogRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry) { parent::__construct($registry, AppLog::class); }
|
||||
|
||||
/** حذف همهی لاگها؛ تعداد ردیفهای حذفشده را برمیگرداند. */
|
||||
public function deleteAll(): int
|
||||
{
|
||||
return (int) $this->createQueryBuilder('l')
|
||||
->delete()
|
||||
->getQuery()
|
||||
->execute();
|
||||
}
|
||||
|
||||
/** حذف لاگهای قدیمیتر از timestamp دادهشده؛ تعداد حذفشده را برمیگرداند. */
|
||||
public function deleteOlderThan(int $timestamp): int
|
||||
{
|
||||
return (int) $this->createQueryBuilder('l')
|
||||
->delete()
|
||||
->where('l.createdAt < :ts')
|
||||
->setParameter('ts', $timestamp)
|
||||
->getQuery()
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Logging\Command;
|
||||
|
||||
use App\Shared\Logging\LogPruneService;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand(name: 'app:prune-logs', description: 'حذف لاگهای قدیمیتر از مدت نگهداری تنظیمشده')]
|
||||
final class PruneLogsCommand extends Command
|
||||
{
|
||||
public function __construct(private readonly LogPruneService $pruneService)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$deleted = $this->pruneService->pruneByRetention();
|
||||
$output->writeln(sprintf('%d لاگ حذف شد.', $deleted));
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Logging;
|
||||
|
||||
use App\Config\Repository\SiteConfigRepository;
|
||||
|
||||
/**
|
||||
* حذف لاگهای قدیمیتر از مدت نگهداری تنظیمشده (`log_retention_days`).
|
||||
* مقدار ۰ یا نامعتبر یعنی نگهداری نامحدود — چیزی حذف نمیشود.
|
||||
*/
|
||||
class LogPruneService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AppLogRepository $logRepo,
|
||||
private readonly SiteConfigRepository $configRepo,
|
||||
) {}
|
||||
|
||||
/** تعداد لاگهای حذفشده را برمیگرداند. */
|
||||
public function pruneByRetention(): int
|
||||
{
|
||||
$days = (int) ($this->configRepo->get('log_retention_days') ?? 0);
|
||||
if ($days <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$cutoff = time() - ($days * 86400);
|
||||
|
||||
return $this->logRepo->deleteOlderThan($cutoff);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Logging\Message;
|
||||
|
||||
class PruneLogsMessage
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Logging\MessageHandler;
|
||||
|
||||
use App\Shared\Logging\LogPruneService;
|
||||
use App\Shared\Logging\Message\PruneLogsMessage;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
|
||||
#[AsMessageHandler]
|
||||
final class PruneLogsHandler
|
||||
{
|
||||
public function __construct(private readonly LogPruneService $pruneService) {}
|
||||
|
||||
public function __invoke(PruneLogsMessage $message): void
|
||||
{
|
||||
$this->pruneService->pruneByRetention();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user