create-entity-server 0.0.9 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create.js +26 -8
- package/package.json +1 -1
- package/template/.env.example +20 -3
- package/template/configs/database.json +173 -10
- package/template/configs/jwt.json +1 -0
- package/template/configs/oauth.json +37 -0
- package/template/configs/push.json +26 -0
- package/template/entities/Account/account_audit.json +4 -5
- package/template/entities/README.md +4 -4
- package/template/entities/{Auth → System/Auth}/account.json +0 -14
- package/template/entities/System/system_audit_log.json +14 -8
- package/template/samples/README.md +43 -21
- package/template/samples/browser/entity-server-client.js +453 -0
- package/template/samples/browser/example.html +498 -0
- package/template/samples/entities/01_basic_fields.json +39 -0
- package/template/samples/entities/02_types_and_defaults.json +67 -0
- package/template/samples/entities/03_hash_and_unique.json +33 -0
- package/template/samples/entities/04_fk_and_composite_unique.json +29 -0
- package/template/samples/entities/05_cache.json +55 -0
- package/template/samples/entities/06_history_and_hard_delete.json +60 -0
- package/template/samples/entities/07_license_scope.json +52 -0
- package/template/samples/entities/08_hook_sql.json +52 -0
- package/template/samples/entities/09_hook_entity.json +65 -0
- package/template/samples/entities/10_hook_submit_delete.json +78 -0
- package/template/samples/entities/11_hook_webhook.json +84 -0
- package/template/samples/entities/12_hook_push.json +73 -0
- package/template/samples/entities/13_read_only.json +54 -0
- package/template/samples/entities/14_optimistic_lock.json +29 -0
- package/template/samples/entities/15_reset_defaults.json +94 -0
- package/template/samples/entities/16_isolated_license.json +62 -0
- package/template/samples/entities/README.md +91 -0
- package/template/samples/flutter/lib/entity_server_client.dart +261 -48
- package/template/samples/java/EntityServerClient.java +325 -61
- package/template/samples/java/EntityServerExample.java +4 -3
- package/template/samples/kotlin/EntityServerClient.kt +261 -45
- package/template/samples/node/src/EntityServerClient.js +348 -59
- package/template/samples/node/src/example.js +9 -9
- package/template/samples/php/ci4/Config/EntityServer.php +14 -0
- package/template/samples/php/ci4/Controllers/EntityController.php +202 -0
- package/template/samples/php/ci4/Controllers/ProductController.php +16 -76
- package/template/samples/php/ci4/Libraries/EntityServer.php +352 -60
- package/template/samples/php/laravel/Services/EntityServerService.php +245 -40
- package/template/samples/python/entity_server.py +287 -68
- package/template/samples/python/example.py +7 -6
- package/template/samples/react/src/example.tsx +41 -25
- package/template/samples/swift/EntityServerClient.swift +248 -37
- package/template/scripts/normalize-entities.sh +10 -10
- package/template/scripts/run.ps1 +12 -3
- package/template/scripts/run.sh +120 -37
- package/template/scripts/update-server.ps1 +160 -4
- package/template/scripts/update-server.sh +132 -4
- package/template/samples/react/src/api/entityServerClient.ts +0 -290
- package/template/samples/react/src/hooks/useEntity.ts +0 -105
- /package/template/entities/{Auth → System/Auth}/api_keys.json +0 -0
- /package/template/entities/{Auth → System/Auth}/license.json +0 -0
- /package/template/entities/{Auth → System/Auth}/rbac_roles.json +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace App\Controllers;
|
|
4
|
+
|
|
5
|
+
use App\Controllers\BaseController;
|
|
6
|
+
use App\Libraries\EntityServer;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* EntityServer CRUD를 CI4 컨트롤러에서 공통으로 사용하는 베이스 컨트롤러.
|
|
10
|
+
*
|
|
11
|
+
* 사용법:
|
|
12
|
+
* class ProductController extends EntityController {
|
|
13
|
+
* protected string $entity = 'product';
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
abstract class EntityController extends BaseController
|
|
17
|
+
{
|
|
18
|
+
protected EntityServer $es;
|
|
19
|
+
|
|
20
|
+
/** 대상 엔티티명 (하위 컨트롤러에서 지정) */
|
|
21
|
+
protected string $entity = '';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 푸시 트리거용 엔티티명 (기본: 시스템 엔티티 push_msg)
|
|
25
|
+
*
|
|
26
|
+
* 주의: push 인프라 필수 엔티티는 account_device / push_msg / push_log 이며,
|
|
27
|
+
* 이 값은 "어떤 엔티티 insert로 push hook를 트리거할지"를 결정합니다.
|
|
28
|
+
*/
|
|
29
|
+
protected string $pushEntity = 'push_msg';
|
|
30
|
+
|
|
31
|
+
/** pushEntity에서 수신자를 가리키는 필드명 */
|
|
32
|
+
protected string $pushTargetField = 'account_seq';
|
|
33
|
+
|
|
34
|
+
public function __construct()
|
|
35
|
+
{
|
|
36
|
+
$this->es = new EntityServer();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** GET /{entity}/list?page=1&limit=20 */
|
|
40
|
+
public function list(): string
|
|
41
|
+
{
|
|
42
|
+
$page = (int) ($this->request->getGet('page') ?? 1);
|
|
43
|
+
$limit = (int) ($this->request->getGet('limit') ?? 20);
|
|
44
|
+
|
|
45
|
+
$result = $this->es->list($this->entity, [
|
|
46
|
+
'page' => $page,
|
|
47
|
+
'limit' => $limit,
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
return $this->response->setJSON($result)->getBody();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** GET /{entity}/get/(:num) */
|
|
54
|
+
public function get(int $seq): string
|
|
55
|
+
{
|
|
56
|
+
$result = $this->es->get($this->entity, $seq);
|
|
57
|
+
return $this->response->setJSON($result)->getBody();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** POST /{entity}/query */
|
|
61
|
+
public function query(): string
|
|
62
|
+
{
|
|
63
|
+
try {
|
|
64
|
+
$body = $this->es->readRequestBody($this->request);
|
|
65
|
+
} catch (\Throwable $e) {
|
|
66
|
+
return $this->response->setStatusCode(400)
|
|
67
|
+
->setJSON(['ok' => false, 'message' => $e->getMessage()])
|
|
68
|
+
->getBody();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
$filter = $body['filter'] ?? [];
|
|
72
|
+
$params = [
|
|
73
|
+
'page' => $body['page'] ?? 1,
|
|
74
|
+
'limit' => $body['limit'] ?? 20,
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
$result = $this->es->query($this->entity, $filter, $params);
|
|
78
|
+
return $this->response->setJSON($result)->getBody();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* POST /{entity}/submit
|
|
83
|
+
* POST /{entity}/submit/(:num) (seq route param을 body에 주입)
|
|
84
|
+
*/
|
|
85
|
+
public function submit(?int $seq = null): string
|
|
86
|
+
{
|
|
87
|
+
try {
|
|
88
|
+
$data = $this->es->readRequestBody($this->request);
|
|
89
|
+
} catch (\Throwable $e) {
|
|
90
|
+
return $this->response->setStatusCode(400)
|
|
91
|
+
->setJSON(['ok' => false, 'message' => $e->getMessage()])
|
|
92
|
+
->getBody();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if ($seq !== null) {
|
|
96
|
+
$data['seq'] = $seq;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
$result = $this->es->submit($this->entity, $data);
|
|
100
|
+
return $this->response->setJSON($result)->getBody();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** DELETE /{entity}/delete/(:num) */
|
|
104
|
+
public function delete(int $seq): string
|
|
105
|
+
{
|
|
106
|
+
$result = $this->es->delete($this->entity, $seq);
|
|
107
|
+
return $this->response->setJSON($result)->getBody();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** GET /{entity}/history/(:num) */
|
|
111
|
+
public function history(int $seq): string
|
|
112
|
+
{
|
|
113
|
+
$result = $this->es->history($this->entity, $seq);
|
|
114
|
+
return $this->response->setJSON($result)->getBody();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** POST /{entity}/rollback/(:num) */
|
|
118
|
+
public function rollback(int $historySeq): string
|
|
119
|
+
{
|
|
120
|
+
$result = $this->es->rollback($this->entity, $historySeq);
|
|
121
|
+
return $this->response->setJSON($result)->getBody();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* POST /{entity}/push
|
|
126
|
+
*
|
|
127
|
+
* push hook가 연결된 엔티티(push_msg 등)에 submit하여 푸시를 발행합니다.
|
|
128
|
+
* 요청 예:
|
|
129
|
+
* {
|
|
130
|
+
* "account_seq": 1,
|
|
131
|
+
* "title": "알림 제목",
|
|
132
|
+
* "message": "알림 본문",
|
|
133
|
+
* "ref_entity": "order",
|
|
134
|
+
* "ref_seq": 123,
|
|
135
|
+
* "data": {"order_seq": "123"}
|
|
136
|
+
* }
|
|
137
|
+
*/
|
|
138
|
+
public function push(): string
|
|
139
|
+
{
|
|
140
|
+
try {
|
|
141
|
+
$body = $this->es->readRequestBody($this->request);
|
|
142
|
+
} catch (\Throwable $e) {
|
|
143
|
+
return $this->response->setStatusCode(400)
|
|
144
|
+
->setJSON(['ok' => false, 'message' => $e->getMessage()])
|
|
145
|
+
->getBody();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
$target = (int) ($body[$this->pushTargetField] ?? 0);
|
|
149
|
+
if ($target <= 0) {
|
|
150
|
+
return $this->response->setStatusCode(400)
|
|
151
|
+
->setJSON([
|
|
152
|
+
'ok' => false,
|
|
153
|
+
'message' => sprintf('%s required', $this->pushTargetField),
|
|
154
|
+
])
|
|
155
|
+
->getBody();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
$title = (string) ($body['title'] ?? '');
|
|
159
|
+
$message = (string) ($body['message'] ?? $body['body'] ?? '');
|
|
160
|
+
|
|
161
|
+
$payload = [
|
|
162
|
+
$this->pushTargetField => $target,
|
|
163
|
+
'title' => $title,
|
|
164
|
+
'message' => $message,
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
if (isset($body['ref_entity'])) {
|
|
168
|
+
$payload['ref_entity'] = (string) $body['ref_entity'];
|
|
169
|
+
}
|
|
170
|
+
if (isset($body['ref_seq'])) {
|
|
171
|
+
$payload['ref_seq'] = (int) $body['ref_seq'];
|
|
172
|
+
}
|
|
173
|
+
if (isset($body['data']) && is_array($body['data'])) {
|
|
174
|
+
$payload['data'] = $body['data'];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
$result = $this->es->push($this->pushEntity, $payload);
|
|
178
|
+
return $this->response->setJSON($result)->getBody();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* GET /{entity}/push-log/list?page=1&limit=20&account_seq=1
|
|
183
|
+
*/
|
|
184
|
+
public function pushLogList(): string
|
|
185
|
+
{
|
|
186
|
+
$page = (int) ($this->request->getGet('page') ?? 1);
|
|
187
|
+
$limit = (int) ($this->request->getGet('limit') ?? 20);
|
|
188
|
+
|
|
189
|
+
$params = [
|
|
190
|
+
'page' => $page,
|
|
191
|
+
'limit' => $limit,
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
$accountSeq = (int) ($this->request->getGet('account_seq') ?? 0);
|
|
195
|
+
if ($accountSeq > 0) {
|
|
196
|
+
$params['account_seq'] = $accountSeq;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
$result = $this->es->pushLogList($params);
|
|
200
|
+
return $this->response->setJSON($result)->getBody();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -2,81 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
namespace App\Controllers;
|
|
4
4
|
|
|
5
|
-
use App\Controllers\BaseController;
|
|
6
|
-
use App\Libraries\EntityServer;
|
|
7
|
-
|
|
8
5
|
/**
|
|
9
|
-
*
|
|
6
|
+
* Product 전용 컨트롤러 예시.
|
|
7
|
+
* 기본 CRUD는 EntityController를 상속받아 그대로 사용하고,
|
|
8
|
+
* 확장 기능(order)만 이 컨트롤러에서 구현합니다.
|
|
10
9
|
*/
|
|
11
|
-
class ProductController extends
|
|
10
|
+
class ProductController extends EntityController
|
|
12
11
|
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
public function __construct()
|
|
16
|
-
{
|
|
17
|
-
$this->es = new EntityServer();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** GET /products */
|
|
21
|
-
public function index(): string
|
|
22
|
-
{
|
|
23
|
-
$page = (int) ($this->request->getGet('page') ?? 1);
|
|
24
|
-
$limit = (int) ($this->request->getGet('limit') ?? 20);
|
|
25
|
-
$result = $this->es->list('product', ['page' => $page, 'limit' => $limit]);
|
|
26
|
-
|
|
27
|
-
return $this->response->setJSON($result)->getBody();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** GET /products/(:num) */
|
|
31
|
-
public function show(int $seq): string
|
|
32
|
-
{
|
|
33
|
-
$result = $this->es->get('product', $seq);
|
|
34
|
-
return $this->response->setJSON($result)->getBody();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** POST /products/search */
|
|
38
|
-
public function search(): string
|
|
39
|
-
{
|
|
40
|
-
$body = $this->request->getJSON(true);
|
|
41
|
-
$filter = $body['filter'] ?? [];
|
|
42
|
-
$params = ['page' => $body['page'] ?? 1, 'limit' => $body['limit'] ?? 20];
|
|
43
|
-
|
|
44
|
-
// 필터 예: [['field' => 'category', 'op' => 'eq', 'value' => 'electronics']]
|
|
45
|
-
$result = $this->es->query('product', $filter, $params);
|
|
46
|
-
return $this->response->setJSON($result)->getBody();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** POST /products */
|
|
50
|
-
public function create(): string
|
|
51
|
-
{
|
|
52
|
-
$data = $this->request->getJSON(true);
|
|
53
|
-
// seq 없이 submit → 생성
|
|
54
|
-
$result = $this->es->submit('product', $data);
|
|
55
|
-
return $this->response->setStatusCode(201)->setJSON($result)->getBody();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** PUT /products/(:num) */
|
|
59
|
-
public function update(int $seq): string
|
|
60
|
-
{
|
|
61
|
-
$data = $this->request->getJSON(true);
|
|
62
|
-
$data['seq'] = $seq; // seq 포함 → 수정
|
|
63
|
-
$result = $this->es->submit('product', $data);
|
|
64
|
-
return $this->response->setJSON($result)->getBody();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** DELETE /products/(:num) */
|
|
68
|
-
public function delete(int $seq): string
|
|
69
|
-
{
|
|
70
|
-
$result = $this->es->delete('product', $seq);
|
|
71
|
-
return $this->response->setJSON($result)->getBody();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** GET /products/(:num)/history */
|
|
75
|
-
public function history(int $seq): string
|
|
76
|
-
{
|
|
77
|
-
$result = $this->es->history('product', $seq);
|
|
78
|
-
return $this->response->setJSON($result)->getBody();
|
|
79
|
-
}
|
|
12
|
+
protected string $entity = 'product';
|
|
80
13
|
|
|
81
14
|
/**
|
|
82
15
|
* POST /products/order
|
|
@@ -90,7 +23,14 @@ class ProductController extends BaseController
|
|
|
90
23
|
*/
|
|
91
24
|
public function order(): string
|
|
92
25
|
{
|
|
93
|
-
|
|
26
|
+
try {
|
|
27
|
+
$body = $this->es->readRequestBody($this->request);
|
|
28
|
+
} catch (\Throwable $e) {
|
|
29
|
+
return $this->response->setStatusCode(400)
|
|
30
|
+
->setJSON(['ok' => false, 'message' => $e->getMessage()])
|
|
31
|
+
->getBody();
|
|
32
|
+
}
|
|
33
|
+
|
|
94
34
|
$productSeq = (int) ($body['product_seq'] ?? 0);
|
|
95
35
|
$qty = (int) ($body['qty'] ?? 1);
|
|
96
36
|
$buyer = $body['buyer'] ?? '';
|
|
@@ -101,16 +41,16 @@ class ProductController extends BaseController
|
|
|
101
41
|
->getBody();
|
|
102
42
|
}
|
|
103
43
|
|
|
104
|
-
$this->es->transStart(); // 서버 큐 등록, 이후 submit / delete
|
|
44
|
+
$this->es->transStart(); // 서버 큐 등록, 이후 submit / delete 는 큐에 적재
|
|
105
45
|
|
|
106
46
|
try {
|
|
107
47
|
// 1) 상품 조회 후 재고 차감
|
|
108
|
-
$product = $this->es->get(
|
|
48
|
+
$product = $this->es->get($this->entity, $productSeq);
|
|
109
49
|
$stock = (int) ($product['data']['stock'] ?? 0);
|
|
110
50
|
if ($stock < $qty) {
|
|
111
51
|
throw new \RuntimeException('재고 부족');
|
|
112
52
|
}
|
|
113
|
-
$this->es->submit(
|
|
53
|
+
$this->es->submit($this->entity, [
|
|
114
54
|
'seq' => $productSeq,
|
|
115
55
|
'stock' => $stock - $qty,
|
|
116
56
|
]);
|