diff --git a/docs/送检功能操作手册.docx b/docs/送检功能操作手册.docx new file mode 100644 index 0000000..cf0bdfd Binary files /dev/null and b/docs/送检功能操作手册.docx differ diff --git a/docs/送检功能操作手册.pdf b/docs/送检功能操作手册.pdf new file mode 100644 index 0000000..7257e0b Binary files /dev/null and b/docs/送检功能操作手册.pdf differ diff --git a/sql/message.sql b/sql/message.sql index 3af1718..0423092 100644 --- a/sql/message.sql +++ b/sql/message.sql @@ -2,7 +2,7 @@ CREATE TABLE `message` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `patient_id` INT UNSIGNED NOT NULL COMMENT '患者ID', - `type` TINYINT NOT NULL DEFAULT 0 COMMENT '类型:1=审核通过 2=审核拒绝', + `type` TINYINT NOT NULL DEFAULT 0 COMMENT '类型:1=审核通过 2=审核拒绝 3=回寄信息', `title` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '标题', `content` TEXT COMMENT '详细内容', `reason` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '驳回原因', diff --git a/sql/sample_receiver_info.sql b/sql/sample_receiver_info.sql new file mode 100644 index 0000000..5744cf9 --- /dev/null +++ b/sql/sample_receiver_info.sql @@ -0,0 +1,18 @@ +-- 送检信息配置与编辑状态 +ALTER TABLE `patient` + ADD COLUMN `sample_info_status` tinyint(1) DEFAULT 0 COMMENT '送检信息状态: 0可修改 1已生效' AFTER `sample_photos`; + +INSERT INTO `sys_config` (`config_key`, `config_value`, `remark`) VALUES +('sample_receiver_info', '{"address":"武汉东湖新技术开发区花城大道8号武汉软件新城三期D2栋17层001室","receiver":"样本中心","phone":"027-6552 6665","contact_phone":""}', '送检收件信息配置') +ON DUPLICATE KEY UPDATE `remark` = VALUES(`remark`); + +UPDATE `patient` +SET `sample_info_status` = CASE + WHEN COALESCE(`sample_types`, '') <> '' + OR COALESCE(`report_email`, '') <> '' + OR COALESCE(`sample_tracking_no`, '') <> '' + OR COALESCE(JSON_LENGTH(`sample_photos`), 0) > 0 + THEN 1 + ELSE 0 +END +WHERE `is_deleted` = 0; diff --git a/sql/sample_return_tracking.sql b/sql/sample_return_tracking.sql new file mode 100644 index 0000000..8db835b --- /dev/null +++ b/sql/sample_return_tracking.sql @@ -0,0 +1,5 @@ +-- 送检样本回寄物流信息 +ALTER TABLE `patient` + MODIFY COLUMN `sample_info_status` tinyint(1) DEFAULT 0 COMMENT '送检信息状态: 0可修改 1已生效 2已寄回', + ADD COLUMN `return_tracking_no` varchar(100) DEFAULT '' COMMENT '回寄物流单号' AFTER `sample_info_status`, + ADD COLUMN `return_time` datetime DEFAULT NULL COMMENT '回寄时间' AFTER `return_tracking_no`; diff --git a/src/config/router.js b/src/config/router.js index 2569b5d..d9aaceb 100644 --- a/src/config/router.js +++ b/src/config/router.js @@ -50,6 +50,10 @@ module.exports = [ ['/admin/patient/edit', 'admin/patient/edit', 'post'], ['/admin/patient/approve', 'admin/patient/approve', 'post'], ['/admin/patient/reject', 'admin/patient/reject', 'post'], + ['/admin/patient/sampleReceiverConfig', 'admin/patient/sampleReceiverConfig'], + ['/admin/patient/saveSampleReceiverConfig', 'admin/patient/saveSampleReceiverConfig', 'post'], + ['/admin/patient/resetSampleInfo', 'admin/patient/resetSampleInfo', 'post'], + ['/admin/patient/saveReturnTrackingNo', 'admin/patient/saveReturnTrackingNo', 'post'], ['/admin/patient/export', 'admin/patient/export'], // 公共接口 ['/common/regions', 'common/regions'], diff --git a/src/controller/admin/patient.js b/src/controller/admin/patient.js index e451ad7..d143f90 100644 --- a/src/controller/admin/patient.js +++ b/src/controller/admin/patient.js @@ -1,7 +1,13 @@ const Base = require('../base.js'); const dayjs = require('dayjs'); +const AUDIT_APPROVED_CONTENT = '您提交的肠愈同行患者关爱项目资料已审核通过,请于7天内在小程序-个人中心-送检信息中提交相关信息。'; + module.exports = class extends Base { + _canEditPatient() { + return this.isSuperAdmin || (this.userPermissions || []).includes('patient:edit'); + } + // 患者列表页面 async indexAction() { this.assign('currentPage', 'patient'); @@ -74,6 +80,7 @@ module.exports = class extends Base { this.assign('patientId', id); this.assign('adminUser', this.adminUser || {}); this.assign('canAudit', this.isSuperAdmin || (this.userPermissions || []).includes('patient:audit')); + this.assign('canEdit', this._canEditPatient()); return this.display(); } @@ -127,6 +134,25 @@ module.exports = class extends Base { return this.success({ patient, audits }); } + async sampleReceiverConfigAction() { + const config = await this.model('sys_config').getSampleReceiverInfo(); + return this.success(config); + } + + async saveSampleReceiverConfigAction() { + if (!this._canEditPatient()) return this.fail('暂无权限'); + const { address, receiver, phone, contact_phone } = this.post(); + if (!address || !address.trim()) return this.fail('请填写收件地址'); + if (!receiver || !receiver.trim()) return this.fail('请填写收件人'); + if (!phone || !phone.trim()) return this.fail('请填写电话'); + + await this.model('sys_config').setSampleReceiverInfo({ address, receiver, phone, contact_phone }); + await this.log('edit', '患者管理', '编辑送检信息配置'); + + const config = await this.model('sys_config').getSampleReceiverInfo(); + return this.success(config); + } + // 新增患者 async addAction() { const data = this.post(); @@ -253,7 +279,7 @@ module.exports = class extends Base { patient_id: id, type: 1, title: '审核通过', - content: '您提交的个人资料已审核通过。', + content: AUDIT_APPROVED_CONTENT, reason: '' }); @@ -336,6 +362,57 @@ module.exports = class extends Base { return this.success(); } + async resetSampleInfoAction() { + if (!this._canEditPatient()) return this.fail('暂无权限'); + const { id } = this.post(); + if (!id) return this.fail('参数错误'); + + const patient = await this.model('patient').where({ id, is_deleted: 0 }).find(); + if (think.isEmpty(patient)) return this.fail('患者不存在'); + + await this.model('patient').where({ id }).update({ + sample_info_status: 0, + update_by: this.adminUser?.id || 0 + }); + + await this.log('edit', '患者管理', `重置送检信息编辑权限(ID:${id})`); + return this.success(); + } + + async saveReturnTrackingNoAction() { + if (!this._canEditPatient()) return this.fail('暂无权限'); + const { id, return_tracking_no } = this.post(); + if (!id) return this.fail('参数错误'); + if (!return_tracking_no || !return_tracking_no.trim()) return this.fail('请填写回寄物流单号'); + + const patient = await this.model('patient').where({ id, is_deleted: 0 }).find(); + if (think.isEmpty(patient)) return this.fail('患者不存在'); + if (!patient.wax_return) return this.fail('该患者未选择样本寄回'); + if (![1, 2].includes(Number(patient.sample_info_status))) return this.fail('送检信息未生效,暂不能填写回寄物流单号'); + + const trackingNo = return_tracking_no.trim(); + const now = think.datetime(new Date()); + await this.model('patient').where({ id }).update({ + sample_info_status: 2, + return_tracking_no: trackingNo, + return_time: now, + update_by: this.adminUser?.id || 0 + }); + + await this.model('message').where({ patient_id: id, type: 3 }).delete(); + await this.model('message').add({ + patient_id: id, + type: 3, + title: '回寄信息', + content: '样品已经寄回,请注意查收。', + reason: trackingNo, + is_read: 0 + }); + + await this.log('edit', '患者管理', `填写回寄物流单号(ID:${id}):${trackingNo}`); + return this.success(); + } + // 编辑患者 async editAction() { const data = this.post(); diff --git a/src/controller/mp.js b/src/controller/mp.js index b544565..1af4a09 100644 --- a/src/controller/mp.js +++ b/src/controller/mp.js @@ -6,6 +6,7 @@ const path = require('path'); const cosConfig = require('../config/cos.js'); const APP_REMARK = 'pap_mini_cytx'; +const AUDIT_APPROVED_CONTENT = '您提交的肠愈同行患者关爱项目资料已审核通过,请于7天内在小程序-个人中心-送检信息中提交相关信息。'; module.exports = class extends Base { // POST /api/mp/login @@ -637,6 +638,10 @@ module.exports = class extends Base { // 查患者姓名 const patient = await this.model('patient').field('name').where({ id: user.patient_id }).find(); msg.patient_name = patient ? patient.name : ''; + if (msg.type === 1) { + msg.content = AUDIT_APPROVED_CONTENT; + msg.sample_receiver_info = await this.model('sys_config').getSampleReceiverInfo(); + } return this.json({ code: 0, data: msg }); } catch (error) { think.logger.error('messageDetail error:', error); @@ -698,6 +703,7 @@ module.exports = class extends Base { try { sampleTypes = JSON.parse(patient.sample_types || '[]'); } catch (e) { sampleTypes = []; } let samplePhotos = []; try { samplePhotos = JSON.parse(patient.sample_photos || '[]'); } catch (e) { samplePhotos = []; } + const sampleReceiverInfo = await this.model('sys_config').getSampleReceiverInfo(); return this.json({ code: 0, data: { patient: { @@ -716,7 +722,11 @@ module.exports = class extends Base { return_address: patient.return_address || '', report_email: patient.report_email || '', sample_tracking_no: patient.sample_tracking_no || '', - sample_photos: samplePhotos + sample_photos: samplePhotos, + sample_info_status: Number(patient.sample_info_status) || 0, + return_tracking_no: patient.return_tracking_no || '', + return_time: patient.return_time || '', + sample_receiver_info: sampleReceiverInfo }}); } catch (error) { think.logger.error('sampleInfo error:', error); @@ -739,6 +749,30 @@ module.exports = class extends Base { const patient = await this.model('patient').where({ id: user.patient_id, is_deleted: 0 }).find(); if (think.isEmpty(patient)) return this.json({ code: 1, msg: '患者信息不存在' }); if (patient.status !== 1) return this.json({ code: 1, msg: '请先通过审核' }); + const sampleReceiverInfo = await this.model('sys_config').getSampleReceiverInfo(); + const sampleInfoStatus = Number(patient.sample_info_status) || 0; + if (sampleInfoStatus === 2) { + return this.json({ + code: 1010, + data: { + sample_info_status: 2, + return_tracking_no: patient.return_tracking_no || '', + return_time: patient.return_time || '', + sample_receiver_info: sampleReceiverInfo + }, + msg: `样品已经寄回,回寄物流单号:${patient.return_tracking_no || '—'}。` + }); + } + if (sampleInfoStatus === 1) { + const contactPhone = sampleReceiverInfo.contact_phone || ''; + return this.json({ + code: 1010, + data: { sample_info_status: 1, sample_receiver_info: sampleReceiverInfo }, + msg: contactPhone + ? `送检信息已生效,如需修改请联系平台【${contactPhone}】重置修改权限。` + : '送检信息已生效,如需修改请联系平台重置修改权限。' + }); + } const hasSample = sample_types && sample_types.length > 0; @@ -764,7 +798,8 @@ module.exports = class extends Base { return_address: (hasSample && wax_return) ? (return_address || '') : '', report_email: hasSample ? (report_email || '') : '', sample_tracking_no: hasSample ? (sample_tracking_no || '') : '', - sample_photos: hasSample ? JSON.stringify(sample_photos || []) : '[]' + sample_photos: hasSample ? JSON.stringify(sample_photos || []) : '[]', + sample_info_status: 1 }); return this.json({ code: 0, data: {}, msg: '提交成功' }); diff --git a/src/model/sys_config.js b/src/model/sys_config.js index 95ecdb8..81cf345 100644 --- a/src/model/sys_config.js +++ b/src/model/sys_config.js @@ -1,3 +1,10 @@ +const DEFAULT_SAMPLE_RECEIVER_INFO = { + address: '武汉东湖新技术开发区花城大道8号武汉软件新城三期D2栋17层001室', + receiver: '样本中心', + phone: '027-6552 6665', + contact_phone: '' +}; + module.exports = class extends think.Model { get tableName() { return 'sys_config'; @@ -27,4 +34,22 @@ module.exports = class extends think.Model { } return this.where({ config_key: key }).update({ config_value: val }); } + + async getSampleReceiverInfo() { + const info = await this.getByKey('sample_receiver_info'); + return { + ...DEFAULT_SAMPLE_RECEIVER_INFO, + ...(info && typeof info === 'object' ? info : {}) + }; + } + + async setSampleReceiverInfo(info) { + const value = { + address: (info.address || '').trim(), + receiver: (info.receiver || '').trim(), + phone: (info.phone || '').trim(), + contact_phone: (info.contact_phone || '').trim() + }; + return this.setByKey('sample_receiver_info', value, '送检收件信息配置'); + } }; diff --git a/view/admin/patient_detail.html b/view/admin/patient_detail.html index 02761bf..4598539 100644 --- a/view/admin/patient_detail.html +++ b/view/admin/patient_detail.html @@ -147,6 +147,13 @@

送检信息

+
状态: + + + ${ sampleInfoStatusText(patient.sample_info_status) } + + +
送检样本: