Skema entitas Ticketing Konco. Tabel tickets adalah master; ticket_messages menyimpan pesan chat; ticket_activities audit log perubahan.
tickets
Master tiket bantuan.
Field Tipe Catatan idUUID (PK) ticket_numberinteger Nomor urut (auto-increment) — di-pad ke 4 digit untuk display member_idUUID (FK) → members.id (pemilik tiket) admin_idUUID (FK, nullable) → users.id (admin penanggung jawab) subjectstring Cast CleanHtmlInput (HTML purifier) descriptiontext Cast CleanHtmlInput statusstring pending / open / closedprioritystring (nullable) low / medium / highreopened_atdatetime (nullable) Set saat tiket di-reopen closed_atdatetime (nullable) created_by, updated_byUUID WithAuthortimestamps —
Status derivasi
php Copy function getStatusInfoAttribute ( ) & #123;
$ status = strtolower ( $ this -> status ?? ' open ' );
if ($ status === ' open ' && $ this -> admin_id ) & #123;
$ status = ' on progress ' ;
& #125;
// ...badge color mapping
& #125;
Stored status Display bila admin_id terisi pendingpending (blue) openon progress (warning) — bila ada admin_id closedclosed (red)
Relasi
Method Tipe Target message()HasMany TicketMessageads_account()HasMany TicketAdsAccountmember()BelongsTo Memberadmin()BelongsTo User (guard admin)activities()HasMany TicketActivity
Model booted hooks
Event Trigger Side effect creatingTicket baru Set admin_id = null eksplisit createdTicket tersimpan Log activity “Tiket #{number} telah dibuat” updatedadmin_id berubah & tidak nullTicketHandledNotification + email email.ticket.ongoing + AdminTicketAssignedNotificationupdatedstatus berubah ke closedTicketClosedNotification + email email.ticket.closedupdatedstatus berubah ke open & reopened_at setTicketReopenedNotification + email email.ticket.reopeneddeletingHapus tiket Cek guard admin / status pending, cascade hapus messages & ads_accounts
ticket_messages
Pesan tiket — live chat antara member & admin.
Field Tipe Catatan idUUID (PK) ticket_idUUID (FK) → tickets.id user_idUUID (FK) → users.id (pengirim — member atau admin) messagetext Isi pesan readboolean Status sudah dibaca penerima attachment_idUUID (FK, nullable) → attachments.id created_by, updated_byUUID WithAuthortimestamps —
Event on create
TicketMessageCreated — broadcast ke channel private-ticket.{ticket_id} via Reverb. Frontend dengar event ini untuk append pesan ke chat UI real-time.
Relasi
Method Tipe Target ticket()BelongsTo Ticketuser()BelongsTo User (pengirim)attachment()BelongsTo Attachment
ticket_ads_accounts
Relasi tiket ↔ akun iklan (tiket bisa melibatkan multiple akun).
Field Tipe idUUID (PK) ticket_idUUID (FK) ads_account_idUUID (FK) ads_idstring (cache ID Meta) timestamps —
Accessor Ticket::getAdsAccountListAttribute() — return comma-separated ads_id list.
ticket_activities
Audit log perubahan tiket.
Field Tipe Catatan idUUID (PK) ticket_idUUID (FK) → tickets.id user_idUUID (FK) → users.id (actor) actionstring created / assigned / status_changed / closed / reopenedmessagestring Deskripsi perubahan metadataarray (JSON, nullable) Data tambahan timestamps —
attachments
Lampiran pesan tiket.
Field Tipe Catatan idUUID (PK) user_idUUID (FK) Uploader filenamestring Nama file asli pathstring Path di storage mime_typestring sizeinteger Bytes diskstring local / public / s3timestamps —
Ticket auto-assignment
Jika config ticket_support_role (UUID role) diset, Ticket::getadmin() auto-pilih admin random dengan role tersebut:
php Copy static function getadmin ( ) & #123;
$ cs = Config :: get_val ( ' ticket_support_role ' );
if ( ! Str :: isUuid ($ cs )) return ;
return User :: whereHas ( " roles " , fn ($ q ) => $ q -> where ( " id " , $ cs ))
-> admin () -> active () -> inRandomOrder () -> pluck ( ' id ' ) -> first ();
& #125;
Broadcast channels
php Copy // routes/channels.php
Broadcast :: channel ( ' ticket.{id} ' , function ($ user , $ id ) & #123;
$ticket = Ticket::find ($ id ) ;
return $ticket & & ( $ticket->member_id === $user->member->id ?? null
|| $user->guard_name === 'admin' ) ;
& #125;);
Scopes
Scope Fungsi scopeActive($q)whereIn('status', ['pending', 'open'])scopeUnread($q)Filter pesan belum dibaca — logic berbeda untuk admin vs member
Langkah berikutnya