박권수

feat. 회원탈퇴 api 완성 / 회원수정 api 완성 / MQTT Message 변경 완료 / Qr코드 양식 수정 완료 / 복용량 관련 d…

…b 수정 및 api 수정 완료 / 의사가 번호로 환자 검색 완료 / 핸드폰번호 고유하게
...@@ -5,10 +5,20 @@ ...@@ -5,10 +5,20 @@
5 "version": "0.2.0", 5 "version": "0.2.0",
6 "configurations": [ 6 "configurations": [
7 { 7 {
8 - "name": "flutter_application_1", 8 + "name": "Attach",
9 - "cwd": "frontend\\flutter_application_1", 9 + "port": 9229,
10 - "request": "launch", 10 + "request": "attach",
11 - "type": "dart" 11 + "skipFiles": [
12 - } 12 + "<node_internals>/**"
13 + ],
14 + "type": "pwa-node"
15 + },
16 + {
17 + "name": "Attach to Chrome",
18 + "port": 9222,
19 + "request": "attach",
20 + "type": "pwa-chrome",
21 + "webRoot": "${workspaceFolder}"
22 + },
13 ] 23 ]
14 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -4,6 +4,8 @@ const Profile = require('../../models/profile'); ...@@ -4,6 +4,8 @@ const Profile = require('../../models/profile');
4 const DoctorInfo = require('../../models/doctorInfo'); 4 const DoctorInfo = require('../../models/doctorInfo');
5 const Hub = require('../../models/hub'); 5 const Hub = require('../../models/hub');
6 const Bottle = require('../../models/bottle'); 6 const Bottle = require('../../models/bottle');
7 +const BottleMedicine = require('../../models/bottleMedicine');
8 +const PatientInfo = require('../../models/patientInfo');
7 const { uploadDoctorLicense } = require('../../util/GoogleCloudStorage'); 9 const { uploadDoctorLicense } = require('../../util/GoogleCloudStorage');
8 const Joi = require('joi'); 10 const Joi = require('joi');
9 const jwt = require('jsonwebtoken'); 11 const jwt = require('jsonwebtoken');
...@@ -49,6 +51,15 @@ exports.register = async(ctx) => { ...@@ -49,6 +51,15 @@ exports.register = async(ctx) => {
49 return; 51 return;
50 } 52 }
51 53
54 + const existContact = await Profile.findOne({ contact, useYn : 'Y' });
55 + if(existContact) {
56 + ctx.status = 409;
57 + ctx.body = {
58 + error : '이미 가입된 번호입니다.',
59 + };
60 + return;
61 + }
62 +
52 const user = new User({ 63 const user = new User({
53 userId, 64 userId,
54 userTypeCd : 'NORMAL', 65 userTypeCd : 'NORMAL',
...@@ -190,8 +201,8 @@ exports.doctorRegister = async ctx => { ...@@ -190,8 +201,8 @@ exports.doctorRegister = async ctx => {
190 useYn : 'W', 201 useYn : 'W',
191 }); 202 });
192 203
193 - doctor.save(); 204 + await doctor.save();
194 - doctorInfo.save(); 205 + await doctorInfo.save();
195 206
196 ctx.status = 201; 207 ctx.status = 201;
197 208
...@@ -283,8 +294,8 @@ exports.socialRegister = async ctx => { ...@@ -283,8 +294,8 @@ exports.socialRegister = async ctx => {
283 return { 294 return {
284 userId : result.email, 295 userId : result.email,
285 userNm : result.name, 296 userNm : result.name,
286 - contact : null, 297 + contact : `${result.email}_등록되지않은 번호`,
287 - birth : null, 298 + birth : '등록되지않음',
288 }; 299 };
289 } 300 }
290 : socialType.toUpperCase() === 'NAVER' ? async () => { 301 : socialType.toUpperCase() === 'NAVER' ? async () => {
...@@ -340,6 +351,16 @@ exports.socialRegister = async ctx => { ...@@ -340,6 +351,16 @@ exports.socialRegister = async ctx => {
340 return; 351 return;
341 } 352 }
342 353
354 + const existContact = await Profile.findOne({ contact, useYn : 'Y'});
355 + if(existContact) {
356 + ctx.status = 409;
357 + ctx.body = {
358 + error : '이미 가입된 번호',
359 + };
360 +
361 + return;
362 + }
363 +
343 const user = new User({ 364 const user = new User({
344 userId, 365 userId,
345 hashedPassword : 'unnecessary', 366 hashedPassword : 'unnecessary',
...@@ -458,6 +479,7 @@ exports.logout = async(ctx) => { ...@@ -458,6 +479,7 @@ exports.logout = async(ctx) => {
458 ctx.status = 204; 479 ctx.status = 204;
459 }; 480 };
460 481
482 +
461 /** 483 /**
462 * 회원 탈퇴 484 * 회원 탈퇴
463 * @param {*} ctx 485 * @param {*} ctx
...@@ -492,27 +514,46 @@ exports.secession = async ctx => { ...@@ -492,27 +514,46 @@ exports.secession = async ctx => {
492 if(user.userTypeCd === 'NORMAL') { 514 if(user.userTypeCd === 'NORMAL') {
493 const profile = await Profile.findOne({ userId }); 515 const profile = await Profile.findOne({ userId });
494 516
495 - profile.setUseYn('N'); 517 + //프로필 삭제
496 - profile.save(); 518 + await profile.setUseYn('N');
519 + await profile.save();
497 520
521 + //유저에 등록된 허브, 약병, 약병정보 전부 삭제
498 const hubList = await Hub.find({ userId }); 522 const hubList = await Hub.find({ userId });
499 - await Promise.all(hubList.forEach(async hub => { 523 + await Promise.all(hubList.map(async hub => {
524 + const bottleList = await Bottle.find({ hubId : hub.hubId });
525 + await Promise.all(bottleList.map(async bottle => {
526 + const bottleMedicine = await BottleMedicine.findOne({ bottleId : bottle.bottleId });
527 + await bottleMedicine.setUseYn('N');
528 + await bottleMedicine.save();
529 + }));
530 +
500 await Bottle.deleteMany({ hubId : hub.hubId }); 531 await Bottle.deleteMany({ hubId : hub.hubId });
501 })); 532 }));
502 533
503 await Hub.deleteMany({ userId }); 534 await Hub.deleteMany({ userId });
504 535
505 - user.setUseYn('N'); 536 +
506 - user.save(); 537 + //환자 정보 삭제
538 + const patientInfoList = await PatientInfo.find({ patientId : userId, useYn : 'Y' });
539 + await Promise.all(patientInfoList.map(async patientInfo => {
540 + await patientInfo.setUseYn('N');
541 + await patientInfo.save();
542 + }));
543 +
544 +
545 + //유저 삭제
546 + await user.setUseYn('N');
547 + await user.save();
507 548
508 } else if (user.userTypeCd === 'DOCTOR') { 549 } else if (user.userTypeCd === 'DOCTOR') {
509 const doctorInfo = await DoctorInfo.findOne({ doctorId : userId }); 550 const doctorInfo = await DoctorInfo.findOne({ doctorId : userId });
510 551
511 - doctorInfo.setUseYn('WS'); 552 + await doctorInfo.setUseYn('WS');
512 - doctorInfo.save(); 553 + await doctorInfo.save();
513 554
514 - user.setUseYn('WS'); 555 + await user.setUseYn('WS');
515 - user.save(); 556 + await user.save();
516 } 557 }
517 558
518 ctx.status = 200; 559 ctx.status = 200;
......
...@@ -26,7 +26,7 @@ auth.get('/hospital', authCtrl.searchHospital); ...@@ -26,7 +26,7 @@ auth.get('/hospital', authCtrl.searchHospital);
26 * request parameter : userId, password, passwordCheck, doctorInfo(File) 26 * request parameter : userId, password, passwordCheck, doctorInfo(File)
27 * return : null 27 * return : null
28 */ 28 */
29 - auth.post('/register/doctor', KoaBody, authCtrl.doctorRegister) 29 +auth.post('/register/doctor', KoaBody, authCtrl.doctorRegister)
30 30
31 /** 31 /**
32 * 로컬 로그인 (email type) 32 * 로컬 로그인 (email type)
...@@ -58,7 +58,15 @@ auth.post('/login/social/:socialType', authCtrl.socialLogin); ...@@ -58,7 +58,15 @@ auth.post('/login/social/:socialType', authCtrl.socialLogin);
58 * request parameter : null 58 * request parameter : null
59 * return : null 59 * return : null
60 */ 60 */
61 -auth.post('/logout', authCtrl.logout) 61 +auth.post('/logout', authCtrl.logout);
62 +
63 +/**
64 + * 회원 탈퇴
65 + * url : http://localhost:4000/api/auth
66 + * request parameter : password
67 + * return : null
68 + */
69 +auth.delete('/', authCtrl.secession);
62 70
63 /** 71 /**
64 * 토큰이 유효한지 확인 72 * 토큰이 유효한지 확인
......
1 -/* eslint-disable no-undef */
2 //어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다. 1 //어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다.
3 const Bottle = require('../../models/bottle'); 2 const Bottle = require('../../models/bottle');
4 const Hub = require('../../models/hub'); 3 const Hub = require('../../models/hub');
...@@ -20,6 +19,7 @@ exports.bottleConnect = async(ctx) => { ...@@ -20,6 +19,7 @@ exports.bottleConnect = async(ctx) => {
20 return; 19 return;
21 } 20 }
22 21
22 + // eslint-disable-next-line no-undef
23 const { userId } = jwt.verify(token, process.env.JWT_SECRET); 23 const { userId } = jwt.verify(token, process.env.JWT_SECRET);
24 const user = await User.findByUserId(userId); 24 const user = await User.findByUserId(userId);
25 if(!user || !user.userTypeCd || user.useYn !== 'Y') { 25 if(!user || !user.userTypeCd || user.useYn !== 'Y') {
...@@ -74,6 +74,7 @@ exports.bottleDisconnect = async(ctx) => { ...@@ -74,6 +74,7 @@ exports.bottleDisconnect = async(ctx) => {
74 return; 74 return;
75 } 75 }
76 76
77 + // eslint-disable-next-line no-undef
77 const { userId } = jwt.verify(token, process.env.JWT_SECRET); 78 const { userId } = jwt.verify(token, process.env.JWT_SECRET);
78 const user = await User.findByUserId(userId); 79 const user = await User.findByUserId(userId);
79 if(!user || !user.userTypeCd || user.useYn !== 'Y') { 80 if(!user || !user.userTypeCd || user.useYn !== 'Y') {
...@@ -115,6 +116,7 @@ exports.getBottleInfo = async(ctx) => { ...@@ -115,6 +116,7 @@ exports.getBottleInfo = async(ctx) => {
115 return; 116 return;
116 } 117 }
117 118
119 + // eslint-disable-next-line no-undef
118 const { userId } = jwt.verify(token, process.env.JWT_SECRET); 120 const { userId } = jwt.verify(token, process.env.JWT_SECRET);
119 const user = await User.findByUserId(userId); 121 const user = await User.findByUserId(userId);
120 if(!user || !user.userTypeCd || user.useYn !== 'Y') { 122 if(!user || !user.userTypeCd || user.useYn !== 'Y') {
...@@ -156,20 +158,20 @@ exports.getBottleInfo = async(ctx) => { ...@@ -156,20 +158,20 @@ exports.getBottleInfo = async(ctx) => {
156 158
157 ctx.status = 200; 159 ctx.status = 200;
158 ctx.body = { 160 ctx.body = {
159 - bottle,
160 medicine, 161 medicine,
161 doctorInfo, 162 doctorInfo,
162 - dosage : bottleMedicine.dosage, 163 + dailyDosage : bottleMedicine.dailyDosage,
164 + totalDosage : bottleMedicine.totalDosage,
163 takeMedicineHist, 165 takeMedicineHist,
164 }; 166 };
165 167
166 } else { 168 } else {
167 ctx.status = 200; 169 ctx.status = 200;
168 ctx.body = { 170 ctx.body = {
169 - bottle,
170 medicine : null, 171 medicine : null,
171 doctorInfo : null, 172 doctorInfo : null,
172 - dosage : null, 173 + dailyDosage : null,
174 + totalDosage : null,
173 takeMedicineHist : [], 175 takeMedicineHist : [],
174 } 176 }
175 } 177 }
...@@ -184,6 +186,7 @@ exports.getBottleFeedback = async ctx => { ...@@ -184,6 +186,7 @@ exports.getBottleFeedback = async ctx => {
184 return; 186 return;
185 } 187 }
186 188
189 + // eslint-disable-next-line no-undef
187 const { userId } = jwt.verify(token, process.env.JWT_SECRET); 190 const { userId } = jwt.verify(token, process.env.JWT_SECRET);
188 const user = await User.findByUserId(userId); 191 const user = await User.findByUserId(userId);
189 if(!user || !user.userTypeCd || user.useYn !== 'Y') { 192 if(!user || !user.userTypeCd || user.useYn !== 'Y') {
...@@ -222,7 +225,9 @@ exports.getBottleFeedback = async ctx => { ...@@ -222,7 +225,9 @@ exports.getBottleFeedback = async ctx => {
222 .populate('bmId'); 225 .populate('bmId');
223 226
224 ctx.status = 200; 227 ctx.status = 200;
225 - ctx.body = feedbackList; 228 + ctx.body = {
229 + feedbackList
230 + };
226 } else { 231 } else {
227 ctx.status = 404; 232 ctx.status = 404;
228 ctx.body = { 233 ctx.body = {
...@@ -240,6 +245,7 @@ exports.setMedicine = async(ctx) => { ...@@ -240,6 +245,7 @@ exports.setMedicine = async(ctx) => {
240 return; 245 return;
241 } 246 }
242 247
248 + // eslint-disable-next-line no-undef
243 const { userId } = jwt.verify(token, process.env.JWT_SECRET); 249 const { userId } = jwt.verify(token, process.env.JWT_SECRET);
244 const user = await User.findByUserId(userId); 250 const user = await User.findByUserId(userId);
245 if(!user || !user.userTypeCd || user.useYn !== 'Y') { 251 if(!user || !user.userTypeCd || user.useYn !== 'Y') {
...@@ -248,7 +254,7 @@ exports.setMedicine = async(ctx) => { ...@@ -248,7 +254,7 @@ exports.setMedicine = async(ctx) => {
248 } 254 }
249 255
250 const { bottleId } = ctx.params; 256 const { bottleId } = ctx.params;
251 - const { medicineId, dosage, doctorId } = ctx.request.body; 257 + const { medicineId, doctorId, dailyDosage, totalDosage, } = ctx.request.body;
252 258
253 const bottle = await Bottle.findByBottleId(bottleId); 259 const bottle = await Bottle.findByBottleId(bottleId);
254 if(!bottle) { 260 if(!bottle) {
...@@ -282,7 +288,8 @@ exports.setMedicine = async(ctx) => { ...@@ -282,7 +288,8 @@ exports.setMedicine = async(ctx) => {
282 let bottleMedicine = new BottleMedicine({ 288 let bottleMedicine = new BottleMedicine({
283 bottleId, 289 bottleId,
284 medicineId, 290 medicineId,
285 - dosage, 291 + dailyDosage,
292 + totalDosage,
286 }); 293 });
287 294
288 if(doctorId !== undefined && doctorId !== null && doctorId !== '') { 295 if(doctorId !== undefined && doctorId !== null && doctorId !== '') {
...@@ -295,14 +302,63 @@ exports.setMedicine = async(ctx) => { ...@@ -295,14 +302,63 @@ exports.setMedicine = async(ctx) => {
295 return; 302 return;
296 } 303 }
297 304
298 - bottleMedicine.setDoctorId(doctorId); 305 + await bottleMedicine.setDoctorId(doctorId);
299 } 306 }
300 307
301 await BottleMedicine.updateMany({ bottleId }, { useYn : 'N '}); 308 await BottleMedicine.updateMany({ bottleId }, { useYn : 'N '});
302 309
303 - bottleMedicine.save(); 310 + await bottleMedicine.save();
311 +
312 + ctx.status = 200;
313 +};
314 +
315 +//약 무게 세팅
316 +exports.setMedicineWeight = async ctx => {
317 + const token = ctx.req.headers.authorization;
318 + if(!token || !token.length) {
319 + ctx.status = 401;
320 + return;
321 + }
322 +
323 + // eslint-disable-next-line no-undef
324 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
325 + const user = await User.findByUserId(userId);
326 + if(!user || !user.userTypeCd || user.useYn !== 'Y') {
327 + ctx.status = 403;
328 + return;
329 + }
330 +
331 + const { bottleId } = ctx.params;
332 +
333 + const bottle = await Bottle.findByBottleId(bottleId);
334 + if(!bottle) {
335 + ctx.status = 404;
336 + ctx.body = {
337 + error : '약병 찾을 수 없음.',
338 + }
339 + return;
340 + }
341 +
342 + const hub = await Hub.findByHubId(bottle.getHubId());
343 + if(hub.getHub_UserId() !== userId) {
344 + ctx.status = 403;
345 + ctx.body = {
346 + error : '해당 허브 권한 없음',
347 + }
348 + return;
349 + }
350 +
351 +
352 +
353 + //toDo : 약병에서 가져온 무게 데이터를 이용하여, bottleMedicine값을 갱신.
354 +
355 +
356 + const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' });
357 + const { totalWeight, totalDosage } = bottleMedicine;
358 +
304 359
305 ctx.status = 200; 360 ctx.status = 200;
361 +
306 }; 362 };
307 363
308 // //비어있는 약병에 의사를 등록한다. 364 // //비어있는 약병에 의사를 등록한다.
...@@ -353,6 +409,7 @@ exports.getHubsBottleList = async(ctx) => { ...@@ -353,6 +409,7 @@ exports.getHubsBottleList = async(ctx) => {
353 return; 409 return;
354 } 410 }
355 411
412 + // eslint-disable-next-line no-undef
356 const { userId } = jwt.verify(token, process.env.JWT_SECRET); 413 const { userId } = jwt.verify(token, process.env.JWT_SECRET);
357 const user = await User.findByUserId(userId); 414 const user = await User.findByUserId(userId);
358 if(!user || !user.userTypeCd || user.useYn !== 'Y') { 415 if(!user || !user.userTypeCd || user.useYn !== 'Y') {
...@@ -394,6 +451,7 @@ exports.getAllBottleList = async ctx => { ...@@ -394,6 +451,7 @@ exports.getAllBottleList = async ctx => {
394 return; 451 return;
395 } 452 }
396 453
454 + // eslint-disable-next-line no-undef
397 const { userId } = jwt.verify(token, process.env.JWT_SECRET); 455 const { userId } = jwt.verify(token, process.env.JWT_SECRET);
398 const user = await User.findByUserId(userId); 456 const user = await User.findByUserId(userId);
399 if(!user || !user.userTypeCd || user.useYn !== 'Y') { 457 if(!user || !user.userTypeCd || user.useYn !== 'Y') {
...@@ -411,7 +469,7 @@ exports.getAllBottleList = async ctx => { ...@@ -411,7 +469,7 @@ exports.getAllBottleList = async ctx => {
411 469
412 ctx.status = 200; 470 ctx.status = 200;
413 ctx.body = { 471 ctx.body = {
414 - bottleList 472 + bottleList,
415 }; 473 };
416 474
417 }; 475 };
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -44,6 +44,14 @@ bottle.get('/feedback/:bottleId', bottleCtrl.getBottleFeedback); ...@@ -44,6 +44,14 @@ bottle.get('/feedback/:bottleId', bottleCtrl.getBottleFeedback);
44 bottle.patch('/:bottleId', bottleCtrl.setMedicine); 44 bottle.patch('/:bottleId', bottleCtrl.setMedicine);
45 45
46 /** 46 /**
47 + * 약병에 등록된 약의 무게 갱신
48 + * request parameter : bottleid
49 + * url : http://localhost:4000/api/bottle/weight/:bottleId
50 + * return : null
51 + */
52 +bottle.patch('/weight/:bottleId', bottleCtrl.setMedicineWeight);
53 +
54 +/**
47 * 비어있는 약병에 전담의 등록 55 * 비어있는 약병에 전담의 등록
48 * request parameter : bottleId, doctorId 56 * request parameter : bottleId, doctorId
49 * url : http://localhost:4000/api/bottle/doctor/:bottleId 57 * url : http://localhost:4000/api/bottle/doctor/:bottleId
......
...@@ -13,7 +13,7 @@ const PrescribeInfo = require('../../models/prescribeInfo'); ...@@ -13,7 +13,7 @@ const PrescribeInfo = require('../../models/prescribeInfo');
13 13
14 const jwt = require('jsonwebtoken'); 14 const jwt = require('jsonwebtoken');
15 15
16 -const { uploadQrCode, viewQrCode } = require('../../util/GoogleCloudStorage'); 16 +const { uploadQrCode, getQrCodeUrl } = require('../../util/GoogleCloudStorage');
17 const QrCodeUtil = require('../../util/QrCodeUtil'); 17 const QrCodeUtil = require('../../util/QrCodeUtil');
18 const { sendPushMessage } = require('../../util/FCM'); 18 const { sendPushMessage } = require('../../util/FCM');
19 19
...@@ -287,7 +287,7 @@ exports.writeReqPatientReport = async ctx => { ...@@ -287,7 +287,7 @@ exports.writeReqPatientReport = async ctx => {
287 } 287 }
288 288
289 await patientInfo.updateInfo(info); 289 await patientInfo.updateInfo(info);
290 - patientInfo.save(); 290 + await patientInfo.save();
291 291
292 ctx.status = 200; 292 ctx.status = 200;
293 293
...@@ -365,7 +365,7 @@ exports.writeReqBottleFeedback = async ctx => { ...@@ -365,7 +365,7 @@ exports.writeReqBottleFeedback = async ctx => {
365 * @param {*} ctx 365 * @param {*} ctx
366 * @returns 366 * @returns
367 */ 367 */
368 -exports.searchPatientById = async ctx => { 368 +exports.searchPatientByContact = async ctx => {
369 const token = ctx.req.headers.authorization; 369 const token = ctx.req.headers.authorization;
370 if (!token || !token.length) { 370 if (!token || !token.length) {
371 ctx.status = 401; 371 ctx.status = 401;
...@@ -383,22 +383,19 @@ exports.searchPatientById = async ctx => { ...@@ -383,22 +383,19 @@ exports.searchPatientById = async ctx => {
383 return; 383 return;
384 } 384 }
385 385
386 - const { patientId } = ctx.params; 386 + const { contact } = ctx.params;
387 - const patient = await User.findByUserId(patientId); 387 + const patientProfile = await Profile.findOne({ contact, useYn : 'Y' });
388 - if(!patient || patient.useYn !== 'Y') {
389 - ctx.status = 404;
390 - ctx.body = {
391 - error : '존재하지 않는 회원',
392 - };
393 - return;
394 - }
395 388
396 - const patientProfile = await Profile.findOne({ userId : patientId }); 389 + const patientInfo = {
390 + userId : patientProfile.userId,
391 + userNm : patientProfile.userNm,
392 + birth : patientProfile.birth,
393 + contact: patientProfile.contact,
394 + };
397 395
398 ctx.status = 200; 396 ctx.status = 200;
399 ctx.body = { 397 ctx.body = {
400 - patientNm : patientProfile.userNm, 398 + patientInfo,
401 - patientId,
402 }; 399 };
403 }; 400 };
404 401
...@@ -451,8 +448,8 @@ exports.registerNewPatient = async ctx => { ...@@ -451,8 +448,8 @@ exports.registerNewPatient = async ctx => {
451 useYn : 'W', 448 useYn : 'W',
452 }); 449 });
453 450
454 - patientInfo.updateInfo('환자 등록 요청'); 451 + await patientInfo.updateInfo('환자 등록 요청');
455 - patientInfo.save(); 452 + await patientInfo.save();
456 453
457 ctx.status = 200; 454 ctx.status = 200;
458 455
...@@ -501,7 +498,7 @@ exports.removeReqPatient = async ctx => { ...@@ -501,7 +498,7 @@ exports.removeReqPatient = async ctx => {
501 } 498 }
502 499
503 await patientInfo.setUseYn('N') 500 await patientInfo.setUseYn('N')
504 - patientInfo.save(); 501 + await patientInfo.save();
505 502
506 ctx.status = 200; 503 ctx.status = 200;
507 504
...@@ -534,7 +531,8 @@ exports.prescribeMedicine = async ctx => { ...@@ -534,7 +531,8 @@ exports.prescribeMedicine = async ctx => {
534 const { 531 const {
535 patientId, 532 patientId,
536 medicineId, 533 medicineId,
537 - dosage, 534 + dailyDosage,
535 + totalDosage,
538 } = ctx.request.body; 536 } = ctx.request.body;
539 537
540 538
...@@ -551,9 +549,7 @@ exports.prescribeMedicine = async ctx => { ...@@ -551,9 +549,7 @@ exports.prescribeMedicine = async ctx => {
551 return; 549 return;
552 } 550 }
553 551
554 - const medicine = await Medicine.findOne({ 552 + const medicine = await Medicine.findOne({ medicineId });
555 - medicineId
556 - });
557 if(!medicine) { 553 if(!medicine) {
558 ctx.status = 404; 554 ctx.status = 404;
559 ctx.body = { 555 ctx.body = {
...@@ -565,7 +561,8 @@ exports.prescribeMedicine = async ctx => { ...@@ -565,7 +561,8 @@ exports.prescribeMedicine = async ctx => {
565 561
566 const qrCodeResult = await QrCodeUtil.generateQrCode_prescribe({ 562 const qrCodeResult = await QrCodeUtil.generateQrCode_prescribe({
567 medicine, 563 medicine,
568 - dosage, 564 + dailyDosage,
565 + totalDosage,
569 patientId, 566 patientId,
570 doctorId : userId, 567 doctorId : userId,
571 }); 568 });
...@@ -589,19 +586,20 @@ exports.prescribeMedicine = async ctx => { ...@@ -589,19 +586,20 @@ exports.prescribeMedicine = async ctx => {
589 const prescribeInfo = new PrescribeInfo({ 586 const prescribeInfo = new PrescribeInfo({
590 doctorId : userId, 587 doctorId : userId,
591 patientId, 588 patientId,
592 - dosage, 589 + dailyDosage,
590 + totalDosage,
593 medicineId, 591 medicineId,
594 qrCodeUrl, 592 qrCodeUrl,
595 }); 593 });
596 await prescribeInfo.save(); 594 await prescribeInfo.save();
597 595
598 //특이사항에 처방기록 저장 596 //특이사항에 처방기록 저장
599 - patientInfo.updateInfo(`${medicine.name}, 하루 ${dosage} 처방`); 597 + await patientInfo.updateInfo(`${medicine.name}, 하루 ${dailyDosage}회분 처방, 총 ${totalDosage}회분 처방`);
600 await patientInfo.save(); 598 await patientInfo.save();
601 599
602 600
603 const { qrCodeFileName } = qrCodeResult; 601 const { qrCodeFileName } = qrCodeResult;
604 - const qrCode = await viewQrCode({ qrCodeFileName }); 602 + const qrCode = await getQrCodeUrl({ qrCodeFileName });
605 603
606 ctx.status = 200; 604 ctx.status = 200;
607 ctx.body = { 605 ctx.body = {
......
...@@ -58,7 +58,7 @@ doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); ...@@ -58,7 +58,7 @@ doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback);
58 * url : http://localhost:4000/api/api/doctor/patient/search/:patientId 58 * url : http://localhost:4000/api/api/doctor/patient/search/:patientId
59 * return : patient Info(simple) 59 * return : patient Info(simple)
60 */ 60 */
61 -doctor.get('/patient/search/:patientId', doctorCtrl.searchPatientById); 61 +doctor.get('/patient/search/:contact', doctorCtrl.searchPatientByContact);
62 62
63 /** 63 /**
64 * 현재 로그인한 유저(의사)의 관리 환자를 등록함. 64 * 현재 로그인한 유저(의사)의 관리 환자를 등록함.
......
...@@ -21,7 +21,7 @@ exports.hubConnect = async (ctx) => { ...@@ -21,7 +21,7 @@ exports.hubConnect = async (ctx) => {
21 return; 21 return;
22 } 22 }
23 23
24 - const { hubId, host, port } = ctx.request.body; 24 + const { hubId, host } = ctx.request.body;
25 25
26 const isExistHub = await Hub.findByHubId(hubId); 26 const isExistHub = await Hub.findByHubId(hubId);
27 if(isExistHub) { 27 if(isExistHub) {
...@@ -31,7 +31,7 @@ exports.hubConnect = async (ctx) => { ...@@ -31,7 +31,7 @@ exports.hubConnect = async (ctx) => {
31 31
32 const hosting = { 32 const hosting = {
33 host, 33 host,
34 - port 34 + port : "1883",
35 }; 35 };
36 36
37 Mqtt.mqttOn(hosting, DataProcess.dataPublish); 37 Mqtt.mqttOn(hosting, DataProcess.dataPublish);
......
...@@ -206,12 +206,12 @@ exports.acceptDoctorRegReq = async ctx => { ...@@ -206,12 +206,12 @@ exports.acceptDoctorRegReq = async ctx => {
206 useYn : 'W', 206 useYn : 'W',
207 }); 207 });
208 208
209 - doctor.setUseYn('Y'); 209 + await doctor.setUseYn('Y');
210 - doctor.save(); 210 + await doctor.save();
211 211
212 - doctorInfo.setUseYn('Y'); 212 + await doctorInfo.setUseYn('Y');
213 - doctorInfo.setValidateDoctorLicense(validateDoctorLicense); 213 + await doctorInfo.setValidateDoctorLicense(validateDoctorLicense);
214 - doctorInfo.save(); 214 + await doctorInfo.save();
215 215
216 ctx.status = 200; 216 ctx.status = 200;
217 217
...@@ -280,10 +280,10 @@ exports.acceptDoctorRegReq = async ctx => { ...@@ -280,10 +280,10 @@ exports.acceptDoctorRegReq = async ctx => {
280 useYn : 'W', 280 useYn : 'W',
281 }); 281 });
282 282
283 - doctor.setUseYn('N'); 283 + await doctor.setUseYn('N');
284 - doctor.save(); 284 + await doctor.save();
285 - doctorInfo.setUseYn('N'); 285 + await doctorInfo.setUseYn('N');
286 - doctorInfo.save(); 286 + await doctorInfo.save();
287 287
288 ctx.status = 200; 288 ctx.status = 200;
289 289
......
...@@ -11,9 +11,18 @@ const user = new Router(); ...@@ -11,9 +11,18 @@ const user = new Router();
11 */ 11 */
12 user.get('/', userCtrl.getMyDetail); 12 user.get('/', userCtrl.getMyDetail);
13 13
14 +
14 /** 15 /**
15 - * 현재 로그인한 유저에 등록된 의사 목록 가져옴 16 + * 현재 유저 정보 수정
16 * request parameter : token 17 * request parameter : token
18 + * url : http://localhost:4000/api/user
19 + * return : Object User
20 + */
21 +user.patch('/', userCtrl.updateMyDetail);
22 +
23 +/**
24 + * 현재 로그인한 유저에 등록된 의사 목록 가져옴
25 + * request parameter : userNm, birth, contact, password, passwordCheck
17 * url : http://localhost:4000/api/user/doctor 26 * url : http://localhost:4000/api/user/doctor
18 * return : Doctor List 27 * return : Doctor List
19 */ 28 */
......
...@@ -38,10 +38,66 @@ exports.getMyDetail = async ctx => { ...@@ -38,10 +38,66 @@ exports.getMyDetail = async ctx => {
38 /** 38 /**
39 * 내 정보를 업데이트한다. 39 * 내 정보를 업데이트한다.
40 * @param {*} ctx 40 * @param {*} ctx
41 - * http methods : post 41 + * http methods : patch
42 */ 42 */
43 -exports.updateMyInfo = async ctx => { 43 +exports.updateMyDetail = async ctx => {
44 + const token = ctx.req.headers.authorization;
45 + if(!token || !token.length) {
46 + ctx.status = 401;
47 + return;
48 + }
49 +
50 + // eslint-disable-next-line no-undef
51 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
52 + const user = await User.findByUserId(userId);
53 + if(!user || user.useYn !== 'Y' || user.userTypeCd !== 'NORMAL') {
54 + ctx.status = 403;
55 + return;
56 + }
44 57
58 + const profile = await Profile.findByUserId(userId);
59 + if(!profile || profile.useYn !== 'Y') {
60 + ctx.status = 403;
61 + return;
62 + }
63 +
64 + const { userNm, birth, contact, password, passwordCheck, } = ctx.request.body;
65 +
66 + const existContact = await Profile.findOne({ contact, useYn : 'Y' });
67 + if(existContact) {
68 + ctx.status = 409;
69 + ctx.body = {
70 + error : '이미 가입된 번호',
71 + };
72 +
73 + return;
74 + }
75 +
76 + //passwordCheck가 있고 로컬 회원이라면 비밀번호 변경함
77 + if(passwordCheck && user.authTypeCd === 'NORMAL') {
78 + //passwordCheck와 password가 같아야함
79 + if(passwordCheck !== password) {
80 + ctx.status = 401;
81 + ctx.body = {
82 + error : '비밀번호가 일치하지 않습니다.',
83 + };
84 + return;
85 + }
86 +
87 + await user.setPassword(password);
88 + await user.save();
89 + }
90 +
91 + await profile.updateProfileInfo({
92 + userNm,
93 + birth,
94 + contact,
95 + });
96 +
97 + await profile.save();
98 +
99 + ctx.status = 200;
100 +
45 }; 101 };
46 102
47 /** 103 /**
...@@ -76,7 +132,7 @@ exports.getMyDoctorList = async ctx => { ...@@ -76,7 +132,7 @@ exports.getMyDoctorList = async ctx => {
76 useYn : 'Y', 132 useYn : 'Y',
77 }); 133 });
78 134
79 - return doctorInfo.info; 135 + return doctorInfo ? doctorInfo.info : null;
80 })); 136 }));
81 137
82 ctx.status = 200; 138 ctx.status = 200;
...@@ -156,9 +212,9 @@ exports.acceptDoctorRegister = async ctx => { ...@@ -156,9 +212,9 @@ exports.acceptDoctorRegister = async ctx => {
156 return; 212 return;
157 } 213 }
158 214
159 - patientInfo.updateInfo('환자 등록 요청 수락'); 215 + await patientInfo.updateInfo('환자 등록 요청 수락');
160 - patientInfo.setUseYn('Y'); 216 + await patientInfo.setUseYn('Y');
161 - patientInfo.save(); 217 + await patientInfo.save();
162 218
163 ctx.status = 200; 219 ctx.status = 200;
164 220
......
...@@ -16,17 +16,26 @@ const BottleMedicineSchema = new Schema({ ...@@ -16,17 +16,26 @@ const BottleMedicineSchema = new Schema({
16 doctorId : { 16 doctorId : {
17 type : String, 17 type : String,
18 ref : 'User', 18 ref : 'User',
19 - required : true,
20 lowercase : true, 19 lowercase : true,
21 }, 20 },
22 - dosage : { 21 + dailyDosage : {
22 + type : Number,
23 + default : 1,
24 + },
25 + totalDosage : {
26 + type : Number,
27 + default : 1,
28 + },
29 + eachWeight : {
30 + type : Number,
31 + default : 0,
32 + },
33 + totalWeight : {
23 type : Number, 34 type : Number,
24 - required : true,
25 default : 0, 35 default : 0,
26 }, 36 },
27 regDtm : { 37 regDtm : {
28 type : Date, 38 type : Date,
29 - required : true,
30 default : Date.now, 39 default : Date.now,
31 }, 40 },
32 useYn : { 41 useYn : {
...@@ -40,6 +49,14 @@ BottleMedicineSchema.methods.setDoctorId = function(doctorId) { ...@@ -40,6 +49,14 @@ BottleMedicineSchema.methods.setDoctorId = function(doctorId) {
40 this.doctorId = doctorId; 49 this.doctorId = doctorId;
41 }; 50 };
42 51
52 +BottleMedicineSchema.methods.setEachWeight = function(eachWeight) {
53 + this.eachWeight = eachWeight;
54 +};
55 +
56 +BottleMedicineSchema.methods.setTotalWeight = function(totalWeight) {
57 + this.totalWeight = totalWeight;
58 +};
59 +
43 BottleMedicineSchema.methods.setUseYn = function(useYn) { 60 BottleMedicineSchema.methods.setUseYn = function(useYn) {
44 this.useYn = useYn; 61 this.useYn = useYn;
45 }; 62 };
......
...@@ -6,7 +6,8 @@ const PrescribeInfoSchema = new Schema({ ...@@ -6,7 +6,8 @@ const PrescribeInfoSchema = new Schema({
6 doctorId : { type : String, require : true, }, 6 doctorId : { type : String, require : true, },
7 patientId : { type : String, require : true, }, 7 patientId : { type : String, require : true, },
8 medicineId : { type : Number, require : true, }, 8 medicineId : { type : Number, require : true, },
9 - dosage : { type : Number, require : true, }, 9 + dailyDosage : { type : Number, require : true, },
10 + totalDosage : { type : Number, require : true, },
10 qrCodeUrl : { type : String, require : true, }, 11 qrCodeUrl : { type : String, require : true, },
11 }); 12 });
12 13
......
...@@ -15,12 +15,14 @@ ProfileSchema.statics.findByUserId = function(userId) { ...@@ -15,12 +15,14 @@ ProfileSchema.statics.findByUserId = function(userId) {
15 return this.findOne({ userId }); 15 return this.findOne({ userId });
16 }; 16 };
17 17
18 -ProfileSchema.statics.setUseYn = function(useYn) { 18 +ProfileSchema.methods.setUseYn = function(useYn) {
19 this.useYn = useYn; 19 this.useYn = useYn;
20 }; 20 };
21 21
22 -ProfileSchema.methods.updateUserContact = function(contact) { 22 +ProfileSchema.methods.updateProfileInfo = function({ userNm, birth, contact }) {
23 - this.contact = contact; 23 + if(userNm) { this.userNm = userNm }
24 + if(birth) { this.birth = birth }
25 + if(contact) { this.contact = contact }
24 }; 26 };
25 27
26 ProfileSchema.methods.updateDeviceToken = function(deviceToken) { 28 ProfileSchema.methods.updateDeviceToken = function(deviceToken) {
......
...@@ -5,7 +5,6 @@ const Schema = mongoose.Schema; ...@@ -5,7 +5,6 @@ const Schema = mongoose.Schema;
5 const TakeMedicineHistorySchema = new Schema ({ 5 const TakeMedicineHistorySchema = new Schema ({
6 takeDate : { 6 takeDate : {
7 type : Date, 7 type : Date,
8 - required : true,
9 default : Date.now, 8 default : Date.now,
10 }, 9 },
11 bmId : { 10 bmId : {
...@@ -15,7 +14,7 @@ const TakeMedicineHistorySchema = new Schema ({ ...@@ -15,7 +14,7 @@ const TakeMedicineHistorySchema = new Schema ({
15 }, 14 },
16 temperature : { type : Number, default : 0 }, 15 temperature : { type : Number, default : 0 },
17 humidity : { type : Number, default : 0 }, 16 humidity : { type : Number, default : 0 },
18 - balance : { type : Number, default : 0 }, 17 + dosage : { type : Number, default : 0 },
19 }); 18 });
20 19
21 20
......
...@@ -16,66 +16,52 @@ exports.dataPublish = async (topic, message) => { ...@@ -16,66 +16,52 @@ exports.dataPublish = async (topic, message) => {
16 }; 16 };
17 17
18 //Hub topic : bottle/bottleId 18 //Hub topic : bottle/bottleId
19 -//Hub로부터 받은 message : 개폐여부/온도/습도/초음파센서 19 +//Hub로부터 받은 message : 개폐여부/온도/습도/무게센서
20 const factoring = async (topic, message) => { 20 const factoring = async (topic, message) => {
21 const bottleId = parseInt(topic.split('/')[1]); 21 const bottleId = parseInt(topic.split('/')[1]);
22 const data = message.split('/'); 22 const data = message.split('/');
23 - let [isOpen, temperature, humidity, balance] = data; 23 + const [isOpen, humidity, totalWeight, temperature] = data;
24 24
25 - if(isOpen === '1')
26 - balance = await balanceFactoring(balance);
27 - else balance = '-1';
28 -
29 return { 25 return {
30 bottleId, 26 bottleId,
31 isOpen, 27 isOpen,
32 - openDate : new Date(), 28 + temperature,
33 - temperature,
34 humidity, 29 humidity,
35 - balance 30 + totalWeight,
36 }; 31 };
37 32
38 } 33 }
39 34
40 -const balanceFactoring = (balance) => {
41 - const max = 10; //Digital Lead Sensor Maximum Value
42 - const slicingBalance = max / 5;
43 -
44 - if(parseInt(balance) < slicingBalance || parseInt(balance) > max * 2)
45 - return '80';
46 - else if(parseInt(balance) < slicingBalance * 2)
47 - return '60';
48 - else if(parseInt(balance) < slicingBalance * 3)
49 - return '40';
50 - else if(parseInt(balance) < slicingBalance * 4)
51 - return '20';
52 - else return '0';
53 -
54 -}
55 -
56 //bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다. 35 //bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다.
57 const bottleInfoUpdate = async(data) => { 36 const bottleInfoUpdate = async(data) => {
58 - let { bottleId, isOpen, openDate, temperature, humidity, balance } = data; 37 + let { bottleId, isOpen, temperature, humidity, totalWeight } = data;
59 38
60 bottleId = parseInt(bottleId); 39 bottleId = parseInt(bottleId);
61 isOpen = parseInt(isOpen); 40 isOpen = parseInt(isOpen);
62 temperature = parseFloat(temperature); 41 temperature = parseFloat(temperature);
63 humidity = parseFloat(humidity); 42 humidity = parseFloat(humidity);
64 - balance = parseInt(balance); 43 + totalWeight = parseFloat(totalWeight);
65 44
66 const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); 45 const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' });
67 46
68 if(bottleMedicine) { 47 if(bottleMedicine) {
48 + const lastTotalWeight = parseFloat(bottleMedicine.totalWeight);
49 +
69 if(isOpen) { 50 if(isOpen) {
51 + const { eachWeight } = bottleMedicine;
52 + const dosage = Math.round((lastTotalWeight - totalWeight) / parseFloat(eachWeight));
53 +
70 const takeMedicineHist = new TakeMedicineHist({ 54 const takeMedicineHist = new TakeMedicineHist({
71 - takeDate : openDate,
72 bmId : bottleMedicine._id, 55 bmId : bottleMedicine._id,
73 temperature, 56 temperature,
74 humidity, 57 humidity,
75 - balance, 58 + dosage,
76 }); 59 });
77 await takeMedicineHist.save(); 60 await takeMedicineHist.save();
78 } 61 }
62 +
63 + await bottleMedicine.setTotalWeight(totalWeight);
64 + await bottleMedicine.save();
79 } 65 }
80 } 66 }
81 67
...@@ -86,9 +72,9 @@ const transPublishingTopicAndMessage = async(bottleId) => { ...@@ -86,9 +72,9 @@ const transPublishingTopicAndMessage = async(bottleId) => {
86 const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' }); 72 const bottleMedicine = await BottleMedicine.findOne({ bottleId, useYn : 'Y' });
87 const takeMedicineHistList = await TakeMedicineHist.find({ 73 const takeMedicineHistList = await TakeMedicineHist.find({
88 bmId : bottleMedicine._id 74 bmId : bottleMedicine._id
89 - }).sort({ takeDate : 'asc' }); 75 + }).sort({ takeDate : 'asc' }).limit(1);
90 76
91 - const message = 'res/' + await transDate(takeMedicineHistList[0].takeDate) + '/' + bottleMedicine.dosage; 77 + const message = 'res/' + await transDate(takeMedicineHistList[0].takeDate) + '/' + takeMedicineHistList[0].dosage;
92 78
93 return { 79 return {
94 topic, 80 topic,
......
...@@ -57,7 +57,7 @@ exports.uploadQrCode = async ({ directory, qrCodeFileName }) => { ...@@ -57,7 +57,7 @@ exports.uploadQrCode = async ({ directory, qrCodeFileName }) => {
57 }; 57 };
58 58
59 //생성된 QR코드의 signedUrl을 가져옴 59 //생성된 QR코드의 signedUrl을 가져옴
60 -exports.viewQrCode = async ({ qrCodeFileName }) => { 60 +exports.getQrCodeUrl = async ({ qrCodeFileName }) => {
61 const fileName = qrCodeFileName; 61 const fileName = qrCodeFileName;
62 const file = storage.bucket('prescribe-medicine-qrcode').file(fileName); 62 const file = storage.bucket('prescribe-medicine-qrcode').file(fileName);
63 const option = { 63 const option = {
......
...@@ -9,23 +9,23 @@ exports.mqttOn = async (hosting, foo) => { ...@@ -9,23 +9,23 @@ exports.mqttOn = async (hosting, foo) => {
9 }) 9 })
10 10
11 if(filterIndex === -1) { 11 if(filterIndex === -1) {
12 - const client = mqtt.connect(hosting) 12 + const client = mqtt.connect(hosting);
13 - clientList.push(client) 13 + clientList.push(client);
14 14
15 client.on('connect', () => { 15 client.on('connect', () => {
16 console.log('Hub connected: ', client.connected) 16 console.log('Hub connected: ', client.connected)
17 - }) 17 + });
18 18
19 client.on('message', async (topic, message) => { 19 client.on('message', async (topic, message) => {
20 - const result = await foo(topic, message.toString()) 20 + const result = await foo(topic, message.toString());
21 console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m') 21 console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m')
22 - this.mqttPublishMessage(client, result) 22 + this.mqttPublishMessage(client, result);
23 - }) 23 + });
24 24
25 - return client 25 + return client;
26 } 26 }
27 27
28 - return clientList[filterIndex] 28 + return clientList[filterIndex];
29 } 29 }
30 30
31 exports.mqttSubscribe = (client, topic) => { 31 exports.mqttSubscribe = (client, topic) => {
...@@ -49,10 +49,10 @@ exports.mqttOff = (hosting) => { ...@@ -49,10 +49,10 @@ exports.mqttOff = (hosting) => {
49 return (client.options.clientId === hosting.clientId 49 return (client.options.clientId === hosting.clientId
50 && client.options.host === hosting.host 50 && client.options.host === hosting.host
51 && client.options.port === hosting.port) 51 && client.options.port === hosting.port)
52 - }) 52 + });
53 53
54 if(filterIndex !== -1) { 54 if(filterIndex !== -1) {
55 - clientList[filterIndex].end() 55 + clientList[filterIndex].end();
56 - clientList.splice(filterIndex, 1) 56 + clientList.splice(filterIndex, 1);
57 } 57 }
58 } 58 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -2,17 +2,17 @@ const QrCode = require('qrcode'); ...@@ -2,17 +2,17 @@ const QrCode = require('qrcode');
2 const moment = require('moment'); 2 const moment = require('moment');
3 3
4 4
5 -exports.generateQrCode_prescribe = async ({ medicine, dosage, patientId, doctorId }) => { 5 +exports.generateQrCode_prescribe = async ({ medicine, dailyDosage, totalDosage, patientId, doctorId }) => {
6 // eslint-disable-next-line no-undef 6 // eslint-disable-next-line no-undef
7 const directory = process.env.QR_DIR; 7 const directory = process.env.QR_DIR;
8 8
9 const now = moment().format('YYYY-MM-DD_HH:mm'); 9 const now = moment().format('YYYY-MM-DD_HH:mm');
10 - const qrCodeFileName = `${now}_${doctorId}_${patientId}_${medicine.medicineId}_${dosage}.png`; 10 + const qrCodeFileName = `${now}_${doctorId}_${patientId}_${medicine.medicineId}_${dailyDosage}_${totalDosage}.png`;
11 11
12 try { 12 try {
13 await QrCode.toFile( 13 await QrCode.toFile(
14 directory + '/' + qrCodeFileName, 14 directory + '/' + qrCodeFileName,
15 - `${medicine.name}/${medicine.medicineId}/${dosage}/${patientId}/${doctorId}`, 15 + `${medicine.name}/${medicine.medicineId}/${dailyDosage}/${totalDosage}/${patientId}/${doctorId}`,
16 { 16 {
17 color : { 17 color : {
18 dark : '#337DFF', 18 dark : '#337DFF',
......