Customer Management v1.0.0
Source: src/services/customer.service.ts · src/controllers/customer/customer.controller.ts
Key Facts
| Property | Value |
|---|---|
| Route | /customers |
| Scope | Organizer only (NOT merchant-scoped) |
| Auto Role | 010_customer (priority 10) |
| Auto Status | ACTIVATED |
| Auth | JWT + Basic |
| Mapping | 1 PolicyDefinition: USER→ORGANIZER |
Customers are Users with the fixed
CUSTOMERrole. No username, no password, no merchant mapping.
Data Model
PolicyDefinition Records per Customer
| # | variant | subjectType → targetType | targetId | Created By |
|---|---|---|---|---|
| 1 | group | User → Role | 010_customer role ID | UserService.create() |
| 2 | group | User → Organizer | organizer ID | CustomerService._createUserPolicies() |
Creation Flow
Update Flow
Delete
Soft-delete via UserRepository.deleteById() — sets deletedAt timestamp.
Request / Response Schemas
CreateCustomerRequest
Derived from CreateUserRequest with omitted fields:
| Field | Type | Required | Notes |
|---|---|---|---|
emails | string[] | Yes | min 1 |
phones | string[] | Yes | min 1, E.164 |
profile | object | Yes | firstName, lastName, birthday?, locale? |
organizerId | string | Yes | Links customer to organizer |
roleIds,username,credential,statusare omitted — auto-handled by CustomerService.
UpdateByIdCustomerRequest
| Field | Type | Notes |
|---|---|---|
emails | string[]? | min 1 if provided |
phones | string[]? | min 1 if provided |
profile | object? | partial update |
roleIdsandstatusare omitted — customers always keepCUSTOMERrole.
CreateCustomerResponse
typescript
{
id: string,
status: 'ACTIVATED',
isActive: boolean,
createdAt: string,
modifiedAt: string,
profile?: {
firstName, lastName, emails[], phones[],
birthday?, locale, metadata?
}
}Controller Authorization
All customer operations are scoped via UserRepository.findIdsByPoliciesForCurrentUser() with roleIdentifiers: ['011_customer'].
| Operation | Scoping |
|---|---|
find / count / findOne | Filter by policy-scoped IDs + optional organizerId query param |
findById / deleteById | Verify ID in policy-scoped set |
create | Validates organizer ownership via PolicyDefinition |
updateById | Validates organizer co-membership via PolicyDefinition |
deleteBy | Applies id IN scoped IDs filter |
API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /customers | List (organizer-scoped, paginated) |
GET | /customers/count | Count (organizer-scoped) |
GET | /customers/find-one | Find one (organizer-scoped) |
GET | /customers/:id | Get by ID |
POST | /customers | Create customer |
PUT | /customers/:id | Update customer |
DELETE | /customers/:id | Soft-delete |
DELETE | /customers | Bulk delete (organizer-scoped) |
Example Requests
Create
http
POST /v1/api/identity/customers
Authorization: Bearer <token>
{
"emails": ["customer@example.com"],
"phones": ["+84901234567"],
"profile": {
"firstName": "Nguyen",
"lastName": "Van A",
"birthday": "1990-05-15",
"locale": "vi-VN"
},
"organizerId": "org-123"
}Update
http
PUT /v1/api/identity/customers/cust-456
Authorization: Bearer <token>
{
"profile": { "lastName": "Van B" },
"phones": ["+84907654321"]
}Dependencies
Design Decisions
| Decision | Rationale |
|---|---|
| Customer = User + role | Reuses identity infrastructure; consistent auth model |
| Organizer-scoped only | Customers belong to organizers, not individual merchants |
| No login credentials | username + credential omitted from request |
| Soft delete | Preserves transaction history via deletedAt |
Related Pages
- User Management — UserService.create() details
- RBAC — PolicyDefinition model, fixed roles
- Authentication — JWT flow
- Identity Overview — Architecture