Showing
7 changed files
with
226 additions
and
22 deletions
... | @@ -23,7 +23,8 @@ | ... | @@ -23,7 +23,8 @@ |
23 | "moment": "^2.29.1", | 23 | "moment": "^2.29.1", |
24 | "moment-timezone": "^0.5.33", | 24 | "moment-timezone": "^0.5.33", |
25 | "mqtt": "^4.2.6", | 25 | "mqtt": "^4.2.6", |
26 | - "node-cron": "^3.0.0" | 26 | + "node-cron": "^3.0.0", |
27 | + "qrcode": "^1.4.4" | ||
27 | }, | 28 | }, |
28 | "devDependencies": { | 29 | "devDependencies": { |
29 | "eslint": "^7.32.0" | 30 | "eslint": "^7.32.0" | ... | ... |
... | @@ -9,8 +9,13 @@ const Feedback = require('../../models/feedback'); | ... | @@ -9,8 +9,13 @@ const Feedback = require('../../models/feedback'); |
9 | const Hub = require('../../models/hub'); | 9 | const Hub = require('../../models/hub'); |
10 | const PatientInfo = require('../../models/patientInfo'); | 10 | const PatientInfo = require('../../models/patientInfo'); |
11 | const DoctorInfo = require('../../models/doctorInfo'); | 11 | const DoctorInfo = require('../../models/doctorInfo'); |
12 | +const PrescribeInfo = require('../../models/prescribeInfo'); | ||
13 | + | ||
12 | const jwt = require('jsonwebtoken'); | 14 | const jwt = require('jsonwebtoken'); |
13 | 15 | ||
16 | +const { uploadQrCode, viewQrCode } = require('../../util/GoogleCloudStorage'); | ||
17 | +const QrCodeUtil = require('../../util/QrCodeUtil'); | ||
18 | + | ||
14 | 19 | ||
15 | /** | 20 | /** |
16 | * 현재 로그인한 유저의 의사 정보를 가져온다 | 21 | * 현재 로그인한 유저의 의사 정보를 가져온다 |
... | @@ -342,6 +347,11 @@ exports.writeReqBottleFeedback = async ctx => { | ... | @@ -342,6 +347,11 @@ exports.writeReqBottleFeedback = async ctx => { |
342 | 347 | ||
343 | }; | 348 | }; |
344 | 349 | ||
350 | +/** | ||
351 | + * 이메일로 환자를 검색한다. | ||
352 | + * @param {*} ctx | ||
353 | + * @returns | ||
354 | + */ | ||
345 | exports.searchPatientById = async ctx => { | 355 | exports.searchPatientById = async ctx => { |
346 | const token = ctx.req.headers.authorization; | 356 | const token = ctx.req.headers.authorization; |
347 | if (!token || !token.length) { | 357 | if (!token || !token.length) { |
... | @@ -482,4 +492,108 @@ exports.removeReqPatient = async ctx => { | ... | @@ -482,4 +492,108 @@ exports.removeReqPatient = async ctx => { |
482 | 492 | ||
483 | ctx.status = 200; | 493 | ctx.status = 200; |
484 | 494 | ||
495 | +}; | ||
496 | + | ||
497 | +/** | ||
498 | + * 특정 환자에게 특정 약을 처방한다 | ||
499 | + * @param {*} ctx | ||
500 | + * http methods : post | ||
501 | + */ | ||
502 | +exports.prescribeMedicine = async ctx => { | ||
503 | + const token = ctx.req.headers.authorization; | ||
504 | + if(!token || !token.length) { | ||
505 | + ctx.status = 401; | ||
506 | + return; | ||
507 | + } | ||
508 | + | ||
509 | + // eslint-disable-next-line no-undef | ||
510 | + const { userId } = jwt.verify(token, process.env.JWT_SECRET); | ||
511 | + const user = await User.findByUserId(userId); | ||
512 | + if(!user || user.userTypeCd !== 'DOCTOR') { | ||
513 | + ctx.status = 403; | ||
514 | + ctx.body = { | ||
515 | + error : '권한 없는 유저', | ||
516 | + }; | ||
517 | + return; | ||
518 | + } | ||
519 | + | ||
520 | + | ||
521 | + const { | ||
522 | + patientId, | ||
523 | + medicineId, | ||
524 | + dosage, | ||
525 | + } = ctx.request.body; | ||
526 | + | ||
527 | + | ||
528 | + const patientInfo = await PatientInfo.findOne({ | ||
529 | + patientId, | ||
530 | + doctorId : userId, | ||
531 | + useYn : 'Y', | ||
532 | + }) | ||
533 | + if(!patientInfo) { | ||
534 | + ctx.status = 403; | ||
535 | + ctx.body = { | ||
536 | + error : '권한 없는 환자', | ||
537 | + }; | ||
538 | + return; | ||
539 | + } | ||
540 | + | ||
541 | + const medicine = await Medicine.findOne({ | ||
542 | + medicineId | ||
543 | + }); | ||
544 | + if(!medicine) { | ||
545 | + ctx.status = 404; | ||
546 | + ctx.body = { | ||
547 | + error : '존재하지 않는 약', | ||
548 | + }; | ||
549 | + return; | ||
550 | + } | ||
551 | + | ||
552 | + | ||
553 | + const qrCodeResult = await QrCodeUtil.generateQrCode_prescribe({ | ||
554 | + medicine, | ||
555 | + dosage, | ||
556 | + patientId, | ||
557 | + doctorId : userId, | ||
558 | + }); | ||
559 | + if(!qrCodeResult) { | ||
560 | + ctx.status = 400; | ||
561 | + ctx.body = { | ||
562 | + error : 'QR 코드 생성 에러', | ||
563 | + }; | ||
564 | + return; | ||
565 | + } | ||
566 | + | ||
567 | + const qrCodeUrl = await uploadQrCode(qrCodeResult); | ||
568 | + if(!qrCodeUrl) { | ||
569 | + ctx.status = 400; | ||
570 | + ctx.body = { | ||
571 | + error : 'QR 코드 생성 후 서버 업로드 에러', | ||
572 | + }; | ||
573 | + return; | ||
574 | + } | ||
575 | + | ||
576 | + const prescribeInfo = new PrescribeInfo({ | ||
577 | + doctorId : userId, | ||
578 | + patientId, | ||
579 | + dosage, | ||
580 | + medicineId, | ||
581 | + qrCodeUrl, | ||
582 | + }); | ||
583 | + await prescribeInfo.save(); | ||
584 | + | ||
585 | + //특이사항에 처방기록 저장 | ||
586 | + patientInfo.updateInfo(`${medicine.name}, 하루 ${dosage}알 처방`); | ||
587 | + await patientInfo.save(); | ||
588 | + | ||
589 | + | ||
590 | + const { qrCodeFileName } = qrCodeResult; | ||
591 | + const qrCode = await viewQrCode({ qrCodeFileName }); | ||
592 | + | ||
593 | + ctx.status = 200; | ||
594 | + ctx.body = { | ||
595 | + qrCode, | ||
596 | + }; | ||
597 | + | ||
598 | + | ||
485 | }; | 599 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -7,7 +7,7 @@ const doctor = new Router(); | ... | @@ -7,7 +7,7 @@ const doctor = new Router(); |
7 | /** | 7 | /** |
8 | * 현재 로그인한 유저(의사)의 정보를 가져옴. | 8 | * 현재 로그인한 유저(의사)의 정보를 가져옴. |
9 | * request parameter : token | 9 | * request parameter : token |
10 | - * url : http://localhost:4000/doctor/ | 10 | + * url : http://localhost:4000/api/doctor/ |
11 | * return : doctor's Info | 11 | * return : doctor's Info |
12 | */ | 12 | */ |
13 | doctor.get('/', doctorCtrl.getDoctorsInfo); | 13 | doctor.get('/', doctorCtrl.getDoctorsInfo); |
... | @@ -15,7 +15,7 @@ doctor.get('/', doctorCtrl.getDoctorsInfo); | ... | @@ -15,7 +15,7 @@ doctor.get('/', doctorCtrl.getDoctorsInfo); |
15 | /** | 15 | /** |
16 | * 현재 로그인한 유저(의사)의 관리 환자 목록을 가져옴 | 16 | * 현재 로그인한 유저(의사)의 관리 환자 목록을 가져옴 |
17 | * request parameter | 17 | * request parameter |
18 | - * url : http://localhost:4000/doctor/patient | 18 | + * url : http://localhost:4000/api/doctor/patient |
19 | * return : patient List | 19 | * return : patient List |
20 | */ | 20 | */ |
21 | doctor.get('/patient', doctorCtrl.getPatientList); | 21 | doctor.get('/patient', doctorCtrl.getPatientList); |
... | @@ -23,7 +23,7 @@ doctor.get('/patient', doctorCtrl.getPatientList); | ... | @@ -23,7 +23,7 @@ doctor.get('/patient', doctorCtrl.getPatientList); |
23 | /** | 23 | /** |
24 | * 현재 로그인한 유저(의사)의 관리 환자 상세 정보를 가져옴 | 24 | * 현재 로그인한 유저(의사)의 관리 환자 상세 정보를 가져옴 |
25 | * request parameter : patient Id | 25 | * request parameter : patient Id |
26 | - * url : http://localhost:4000/doctor/patient/:patientId | 26 | + * url : http://localhost:4000/api/doctor/patient/:patientId |
27 | * return : patient Detail | 27 | * return : patient Detail |
28 | */ | 28 | */ |
29 | doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); | 29 | doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); |
... | @@ -31,7 +31,7 @@ doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); | ... | @@ -31,7 +31,7 @@ doctor.get('/patient/:patientId', doctorCtrl.getPatientDetail); |
31 | /** | 31 | /** |
32 | * 현재 로그인한 유저(의사)의 관리 약병 상세 정보를 가져옴 | 32 | * 현재 로그인한 유저(의사)의 관리 약병 상세 정보를 가져옴 |
33 | * request parameter : bottle Id | 33 | * request parameter : bottle Id |
34 | - * url : http://localhost:4000/doctor/bottle/:bottleId | 34 | + * url : http://localhost:4000/api/doctor/bottle/:bottleId |
35 | * return : bottle Detail | 35 | * return : bottle Detail |
36 | */ | 36 | */ |
37 | doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); | 37 | doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); |
... | @@ -39,7 +39,7 @@ doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); | ... | @@ -39,7 +39,7 @@ doctor.get('/bottle/:bottleId', doctorCtrl.getBottleDetail); |
39 | /** | 39 | /** |
40 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 특이사항을 기록함 | 40 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 특이사항을 기록함 |
41 | * request parameter : reqUserId, info | 41 | * request parameter : reqUserId, info |
42 | - * url : http://localhost:4000/doctor/patient | 42 | + * url : http://localhost:4000/api/doctor/patient |
43 | * return : null | 43 | * return : null |
44 | */ | 44 | */ |
45 | doctor.patch('/patient', doctorCtrl.writeReqPatientReport); | 45 | doctor.patch('/patient', doctorCtrl.writeReqPatientReport); |
... | @@ -47,7 +47,7 @@ doctor.patch('/patient', doctorCtrl.writeReqPatientReport); | ... | @@ -47,7 +47,7 @@ doctor.patch('/patient', doctorCtrl.writeReqPatientReport); |
47 | /** | 47 | /** |
48 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 약병의 피드백을 등록함. | 48 | * 현재 로그인한 유저(의사)의 특정 관리 환자의 약병의 피드백을 등록함. |
49 | * request parameter : bottleId, fdbType, feedback | 49 | * request parameter : bottleId, fdbType, feedback |
50 | - * url : http://localhost:4000/doctor/bottle | 50 | + * url : http://localhost:4000/api/doctor/bottle |
51 | * return : null | 51 | * return : null |
52 | */ | 52 | */ |
53 | doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); | 53 | doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); |
... | @@ -55,7 +55,7 @@ doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); | ... | @@ -55,7 +55,7 @@ doctor.post('/bottle', doctorCtrl.writeReqBottleFeedback); |
55 | /** | 55 | /** |
56 | * 현재 로그인한 유저(의사)가 이메일로 유저를 검색함 | 56 | * 현재 로그인한 유저(의사)가 이메일로 유저를 검색함 |
57 | * request parameter : patientId | 57 | * request parameter : patientId |
58 | - * url : http://localhost:4000/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/:patientId', doctorCtrl.searchPatientById); |
... | @@ -63,7 +63,7 @@ doctor.get('/patient/search/:patientId', doctorCtrl.searchPatientById); | ... | @@ -63,7 +63,7 @@ doctor.get('/patient/search/:patientId', doctorCtrl.searchPatientById); |
63 | /** | 63 | /** |
64 | * 현재 로그인한 유저(의사)의 관리 환자를 등록함. | 64 | * 현재 로그인한 유저(의사)의 관리 환자를 등록함. |
65 | * request parameter : patientId | 65 | * request parameter : patientId |
66 | - * url : http://localhost:4000/doctor/patient | 66 | + * url : http://localhost:4000/api/doctor/patient |
67 | * return : null | 67 | * return : null |
68 | */ | 68 | */ |
69 | doctor.post('/patient', doctorCtrl.registerNewPatient); | 69 | doctor.post('/patient', doctorCtrl.registerNewPatient); |
... | @@ -71,11 +71,18 @@ doctor.post('/patient', doctorCtrl.registerNewPatient); | ... | @@ -71,11 +71,18 @@ doctor.post('/patient', doctorCtrl.registerNewPatient); |
71 | /** | 71 | /** |
72 | * 현재 로그인한 유저(의사)의 특정 관리 환자를 삭제함. | 72 | * 현재 로그인한 유저(의사)의 특정 관리 환자를 삭제함. |
73 | * request parameter : patientId | 73 | * request parameter : patientId |
74 | - * url : http://localhost:4000/doctor/patient/:patientId | 74 | + * url : http://localhost:4000/api/doctor/patient/:patientId |
75 | * return : null | 75 | * return : null |
76 | */ | 76 | */ |
77 | doctor.delete('/patient/:patientId', doctorCtrl.removeReqPatient); | 77 | doctor.delete('/patient/:patientId', doctorCtrl.removeReqPatient); |
78 | 78 | ||
79 | +/** | ||
80 | + * 의사가 관리하는 환자에 대해 특정 약을 처방함 | ||
81 | + * request paramter : patientId, medicineId, dosage | ||
82 | + * url : http://localhost:4000/api/doctor/prescribe | ||
83 | + * return : null | ||
84 | + */ | ||
85 | +doctor.post('/prescribe', doctorCtrl.prescribeMedicine); | ||
86 | + | ||
79 | 87 | ||
80 | -module.exports = doctor; | ||
81 | - | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
88 | +module.exports = doctor; | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
server/src/models/prescribeInfo.js
0 → 100644
1 | +const mongoose = require('mongoose'); | ||
2 | + | ||
3 | +const Schema = mongoose.Schema; | ||
4 | + | ||
5 | +const PrescribeInfoSchema = new Schema({ | ||
6 | + doctorId : { type : String, require : true, }, | ||
7 | + patientId : { type : String, require : true, }, | ||
8 | + medicineId : { type : Number, require : true, }, | ||
9 | + dosage : { type : Number, require : true, }, | ||
10 | + qrCodeUrl : { type : String, require : true, }, | ||
11 | +}); | ||
12 | + | ||
13 | + | ||
14 | +module.exports = mongoose.model('PrescribeInfo', PrescribeInfoSchema); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | const { Storage } = require('@google-cloud/storage'); | 1 | const { Storage } = require('@google-cloud/storage'); |
2 | +const fs = require('fs'); | ||
2 | 3 | ||
3 | const storage = new Storage(); | 4 | const storage = new Storage(); |
4 | const GoogleStorageUrl = 'https://storage.googleapis.com/'; | 5 | const GoogleStorageUrl = 'https://storage.googleapis.com/'; |
... | @@ -7,19 +8,23 @@ const GoogleStorageUrl = 'https://storage.googleapis.com/'; | ... | @@ -7,19 +8,23 @@ const GoogleStorageUrl = 'https://storage.googleapis.com/'; |
7 | //의사 아이디, 업로드할 파일명, 업로드할 파일 경로를 인자로 받아, File을 GCS에 업로드 후 GCS 주소를 반환 | 8 | //의사 아이디, 업로드할 파일명, 업로드할 파일 경로를 인자로 받아, File을 GCS에 업로드 후 GCS 주소를 반환 |
8 | exports.uploadDoctorLicense = async ({ userId, fileName, filePath }) => { | 9 | exports.uploadDoctorLicense = async ({ userId, fileName, filePath }) => { |
9 | const destination = userId + '_' + fileName; | 10 | const destination = userId + '_' + fileName; |
10 | - const result = await storage.bucket('doctor-info').upload(filePath, { | 11 | + try { |
11 | - destination, | 12 | + const result = await storage.bucket('doctor-info').upload(filePath, { |
12 | - }); | 13 | + destination, |
13 | - | 14 | + }); |
14 | - const doctorLicenseUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; | 15 | + |
15 | - | 16 | + const doctorLicenseUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; |
16 | - return doctorLicenseUrl; | 17 | + |
18 | + return doctorLicenseUrl; | ||
19 | + } catch(e) { | ||
20 | + return null; | ||
21 | + } | ||
17 | }; | 22 | }; |
18 | 23 | ||
19 | //의사 정보를 인자로 받아 해당 Doctor License의 Signed URL을 반환 | 24 | //의사 정보를 인자로 받아 해당 Doctor License의 Signed URL을 반환 |
20 | exports.viewDoctorLicense = async ({ doctorInfo }) => { | 25 | exports.viewDoctorLicense = async ({ doctorInfo }) => { |
21 | const fileName = doctorInfo.info.doctorLicense.split('/').pop(); | 26 | const fileName = doctorInfo.info.doctorLicense.split('/').pop(); |
22 | - const file = new Storage().bucket('doctor-info').file(fileName); | 27 | + const file = storage.bucket('doctor-info').file(fileName); |
23 | const option = { | 28 | const option = { |
24 | version : 'v4', | 29 | version : 'v4', |
25 | expires : Date.now() + 1000 * 60 * 15, | 30 | expires : Date.now() + 1000 * 60 * 15, |
... | @@ -32,6 +37,36 @@ exports.viewDoctorLicense = async ({ doctorInfo }) => { | ... | @@ -32,6 +37,36 @@ exports.viewDoctorLicense = async ({ doctorInfo }) => { |
32 | }; | 37 | }; |
33 | 38 | ||
34 | //의사 ID, 약 ID, 복용량을 인자로 받아, QR Code를 생성 | 39 | //의사 ID, 약 ID, 복용량을 인자로 받아, QR Code를 생성 |
35 | -exports.prescribeMedicine = async ({ doctorId, medicineId, dosage }) => { | 40 | +exports.uploadQrCode = async ({ directory, qrCodeFileName }) => { |
36 | - //toDo | 41 | + const destination = qrCodeFileName; |
42 | + try { | ||
43 | + //파일을 GCS에 업로드 | ||
44 | + const result = await storage.bucket('prescribe-medicine-qrcode').upload(directory + qrCodeFileName, { | ||
45 | + destination | ||
46 | + }); | ||
47 | + | ||
48 | + //업로드 후 파일 삭제 | ||
49 | + fs.rm(directory + qrCodeFileName, () => {}); | ||
50 | + | ||
51 | + const qrCodeUrl = GoogleStorageUrl + `${result[0].bucket.id}/${result[0].name}`; | ||
52 | + | ||
53 | + return qrCodeUrl; | ||
54 | + } catch(e) { | ||
55 | + return null; | ||
56 | + } | ||
57 | +}; | ||
58 | + | ||
59 | +//생성된 QR코드의 signedUrl을 가져옴 | ||
60 | +exports.viewQrCode = async ({ qrCodeFileName }) => { | ||
61 | + const fileName = qrCodeFileName; | ||
62 | + const file = storage.bucket('prescribe-medicine-qrcode').file(fileName); | ||
63 | + const option = { | ||
64 | + version : 'v4', | ||
65 | + expires : Date.now() + 1000 * 60 * 15, | ||
66 | + action : 'read', | ||
67 | + }; | ||
68 | + | ||
69 | + const [signedUrl] = file ? await file.getSignedUrl(option) : [null]; | ||
70 | + | ||
71 | + return signedUrl; | ||
37 | }; | 72 | }; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
server/src/util/QrCodeUtil.js
0 → 100644
1 | +const QrCode = require('qrcode'); | ||
2 | +const moment = require('moment'); | ||
3 | + | ||
4 | + | ||
5 | +exports.generateQrCode_prescribe = async ({ medicine, dosage, patientId, doctorId }) => { | ||
6 | + const directory = "/Users/parkkwonsoo/Desktop/Project/Capstone_Design_1/server/data/"; | ||
7 | + | ||
8 | + const now = moment().format('YYYY-MM-DD_HH:mm'); | ||
9 | + const qrCodeFileName = `${now}_${doctorId}_${patientId}_${medicine.medicineId}_${dosage}.png`; | ||
10 | + | ||
11 | + try { | ||
12 | + await QrCode.toFile( | ||
13 | + directory + qrCodeFileName, | ||
14 | + `${medicine.name}/${medicine.medicineId}/${dosage}/${patientId}/${doctorId}`, | ||
15 | + { | ||
16 | + color : { | ||
17 | + dark : '#337DFF', | ||
18 | + light : '#FFF' | ||
19 | + }, | ||
20 | + } | ||
21 | + ); | ||
22 | + | ||
23 | + return { | ||
24 | + directory, | ||
25 | + qrCodeFileName, | ||
26 | + }; | ||
27 | + | ||
28 | + } catch(e) { | ||
29 | + console.log(e); | ||
30 | + return null; | ||
31 | + } | ||
32 | + | ||
33 | +}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
This diff could not be displayed because it is too large.
-
Please register or login to post a comment