Compare commits
2 Commits
6185f89f95
...
0c369655b7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c369655b7 | ||
|
|
a88b0edd77 |
5
.env.dev
5
.env.dev
@ -23,3 +23,8 @@ PUPPETEER_EXECUTABLE_PATH="C:\Program Files\Google\Chrome\Application\chrome.exe
|
||||
PRESCRIPTION_PDF_MAX_CONCURRENCY=2
|
||||
CONFIG_PRESCRIPTION_PDF_BASE_URL=https://patient.youcan365.com/hn
|
||||
|
||||
## 查询药品信息的时候 带上corpId (测试数据 药品信息存在多家机构, 但是线上各自独立部署, 所以测试环境带corpId区分下)
|
||||
CONFIG_SEARCH_DRUG_WITH_CORPID=YES
|
||||
|
||||
## 生成的咨询订单挂号id编号前缀, 也用于后续生成处方的编号 (his正式环境和测试环境同一个 生成序号规则相同, 产生同编号的话有问题, 所以加个前缀区分下)
|
||||
CONFIG_CONSULT_REGISTER_ID_PREFIX=Test
|
||||
@ -1,52 +0,0 @@
|
||||
CONFIG_DB_USERNAME=youcan
|
||||
CONFIG_DB_PASSWORD=youcanTEST&2025
|
||||
CONFIG_DB_HOST=119.45.106.100
|
||||
CONFIG_DB_PORT=27017
|
||||
CONFIG_NODE_ENV=zytDev
|
||||
CONFIG_CA_BASE_URL=https://newcoss-dev.isignet.cn:10201/coss/service/v1/
|
||||
CONFIG_CA_APPID=APP_7B3F36A14E99410A80B37AEF332E3247
|
||||
CONFIG_CA_SECRET=DLwiH46Esb8ccNTkuSSVAadNTWUfW0sc
|
||||
CONFIG_JWT_SECRET_KEY=ZYTZYY2025
|
||||
CONFIG_JWT_EXPIRE_TIME=12h
|
||||
CONFIG_NODE_PORT=8080
|
||||
CONFIG_ZYB_YB_APPID=7D8D8E29924048539165F5E2E08C4616
|
||||
CONFIG_ZYB_YB_SECRET=77F9610253B94263BCF8DBB61BF90D92
|
||||
CONFIG_SMS_GATEWAY_DISABLED=true
|
||||
|
||||
## 支付宝回调地址
|
||||
CONFIG_ALIPAY_PAY_NOTIFY=https://patient.youcan365.com/zyt/alipay-pay-notify
|
||||
## 支付宝小程序appId
|
||||
CONFIG_ALIPAY_MINI_APPID = 2021005110656858
|
||||
|
||||
## 蜂鸟即时配送请求域名
|
||||
CONFIG_FENGNIAO_API_DOMAIN=https://exam-anubis.ele.me
|
||||
## 蜂鸟即时配送分配的 appId 和 appSecret
|
||||
CONFIG_FENGNIAO_APPID=2711175259951256784
|
||||
CONFIG_FENGNIAO_SECRET=744c587a-61eb-4dc5-a226-e88b7f7b15d0
|
||||
## 蜂鸟即时配送 后台管理页面 -> 应用管理 -> 查看沙箱环境 -> 商户信息的商户ID
|
||||
CONFIG_FENGNIAO_MERCHANT_ID=15649332
|
||||
## 蜂鸟即时配送 后台管理页面 -> 应用管理 -> 查看沙箱环境 -> 门店信息的门店编码
|
||||
CONFIG_FENGNIAO_OUT_SHOP_CODE=testorder001
|
||||
|
||||
## 邮政快递 测试环境请求域名、分配的orgId、AES加密秘钥以及IV
|
||||
CONFIG_EMS_API_DOMAIN=https://topup.beta.ule.com
|
||||
## 邮政快递 测试环境分配的orgId、AES加密秘钥以及IV
|
||||
CONFIG_EMS_ORGID=900004
|
||||
CONFIG_EMS_AES_Key=HOKAwvtyPe9Atve4
|
||||
CONFIG_EMS_AES_IV=HOKAwvtyPe9Atve4
|
||||
## 邮政快递 邮乐分享用户ID (接口文档 2.1订单数据回传接口的入参uleShareUserId字段值, 由对方技术人员提供)
|
||||
CONFIG_EMS_SHARE_USER_ID=yzg10000050008
|
||||
|
||||
## 邮政 请求地址
|
||||
CONFIG_EMS_API_URL=https://api.ems.com.cn/amp-prod-api/f/amp/api/test
|
||||
## 邮政 协议用户编码
|
||||
CONFIG_EMS_USER_CODE=NNLXIQRYV
|
||||
## 邮政 测试授权码
|
||||
CONFIG_EMS_AUTH_CODE=U1pPOYv6VBULDah6
|
||||
## 邮政 测试签名钥匙
|
||||
CONFIG_EMS_SIGN_SECRET=ZmVRRjkyeDZnWHFnUGxkRQ==
|
||||
## 邮政 协议客户号
|
||||
CONFIG_EMS_SENDER_NO=1100100971796
|
||||
|
||||
## 震元his api
|
||||
CONFIG_ZYT_HIS_BASE_URL=http://115.239.161.48:8089
|
||||
16
.env.hn
16
.env.hn
@ -24,3 +24,19 @@ CONFIG_ALIPAY_MINI_PRIVATE_KEY=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBA
|
||||
CONFIG_HN_HIS_BASE_URL=http://122.225.16.74:8026
|
||||
|
||||
|
||||
## im图片存储服务端的位置
|
||||
CONFIG_IM_IMAGE_ROOT=/root/service/hn/private-files/im-chat
|
||||
|
||||
## 处方 PDF(Linux Chrome)
|
||||
PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome
|
||||
PRESCRIPTION_PDF_MAX_CONCURRENCY=2
|
||||
CONFIG_PRESCRIPTION_PDF_ROOT=/root/service/hn/private-files/prescriptions
|
||||
CONFIG_PRESCRIPTION_PDF_BASE_URL=https://patient.youcan365.com/hn
|
||||
|
||||
|
||||
## 查询药品信息的时候 带上corpId (测试数据 药品信息存在多家机构, 但是线上各自独立部署, 所以测试环境带corpId区分下)
|
||||
CONFIG_SEARCH_DRUG_WITH_CORPID=YES
|
||||
|
||||
## 生成的咨询订单挂号id编号前缀, 也用于后续生成处方的编号 (his正式环境和测试环境同一个 生成序号规则相同, 产生同编号的话有问题, 所以加个前缀区分下)
|
||||
CONFIG_CONSULT_REGISTER_ID_PREFIX=Test
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ const TASK_STATUS = {
|
||||
const ERROR_SOURCES = {
|
||||
SYSTEM: "SYSTEM",
|
||||
HIS: "HIS",
|
||||
RATIONAL_DRUG: "RATIONAL_DRUG",
|
||||
};
|
||||
const TASK_CANCEL_REASONS = {
|
||||
HIGH_RISK_DRUG: "HIGH_RISK_DRUG",
|
||||
@ -44,7 +45,9 @@ class AutomationError extends Error {
|
||||
super(message);
|
||||
this.name = "AutomationError";
|
||||
this.retryable = retryable;
|
||||
this.source = source === ERROR_SOURCES.HIS ? ERROR_SOURCES.HIS : ERROR_SOURCES.SYSTEM;
|
||||
this.source = Object.values(ERROR_SOURCES).includes(source)
|
||||
? source
|
||||
: ERROR_SOURCES.SYSTEM;
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,15 +488,17 @@ function cleanHisUploadFailureMessage(message) {
|
||||
|
||||
function getFailureNotificationExt(error) {
|
||||
const errorSource =
|
||||
error && error.source === ERROR_SOURCES.HIS
|
||||
? ERROR_SOURCES.HIS
|
||||
error && Object.values(ERROR_SOURCES).includes(error.source)
|
||||
? error.source
|
||||
: ERROR_SOURCES.SYSTEM;
|
||||
const rawReason =
|
||||
error && typeof error.message === "string" ? error.message.trim() : "";
|
||||
const reason =
|
||||
errorSource === ERROR_SOURCES.HIS
|
||||
? `HIS上传错误:${cleanHisUploadFailureMessage(rawReason) || "未知错误"}`
|
||||
: `系统错误:${rawReason || "未知错误"}`;
|
||||
: errorSource === ERROR_SOURCES.RATIONAL_DRUG
|
||||
? `合理用药错误:${rawReason || "未知错误"}`
|
||||
: `系统错误:${rawReason || "未知错误"}`;
|
||||
return { errorSource, reason };
|
||||
}
|
||||
|
||||
@ -840,6 +845,7 @@ async function resolvePrescriptionDrugs(db, order) {
|
||||
dosage,
|
||||
dosage_unit: dosageUnit.name,
|
||||
dosage_unit_code: dosageUnit.code,
|
||||
dose_time_name: requested.dose_time_name || "",
|
||||
drugName: master.name,
|
||||
specification: master.specification || "",
|
||||
frequencyCode: frequency.code,
|
||||
@ -969,9 +975,9 @@ async function handleAutoOpenRx(db, task) {
|
||||
db
|
||||
);
|
||||
if (!reviewResult || !reviewResult.success) {
|
||||
throw new AutomationError(
|
||||
reviewResult?.message || "合理用药审核未通过"
|
||||
);
|
||||
throw new AutomationError(reviewResult?.message || "合理用药审核未通过", {
|
||||
source: ERROR_SOURCES.RATIONAL_DRUG,
|
||||
});
|
||||
}
|
||||
|
||||
const result = await diagnosticRecord(
|
||||
|
||||
@ -726,6 +726,21 @@ describe("automation task scheduling", () => {
|
||||
}),
|
||||
})
|
||||
);
|
||||
expect(tencentIM).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
msgBody: [
|
||||
expect.objectContaining({
|
||||
MsgContent: expect.objectContaining({
|
||||
Ext: JSON.stringify({
|
||||
errorSource: ERROR_SOURCES.RATIONAL_DRUG,
|
||||
reason: "合理用药错误:该处方存在不合理用药",
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
db
|
||||
);
|
||||
});
|
||||
|
||||
test("fails automatic audit immediately and sends the failure reason", async () => {
|
||||
@ -1012,6 +1027,7 @@ describe("automatic prescription assembly", () => {
|
||||
dosage_form: "片剂",
|
||||
days: 3,
|
||||
dosage_unit: "mg",
|
||||
dose_time_name: "餐后",
|
||||
administration_method: "口服",
|
||||
freq: "每日两次",
|
||||
unit: "盒",
|
||||
@ -1068,6 +1084,7 @@ describe("automatic prescription assembly", () => {
|
||||
_id: drugId.toString(),
|
||||
erpId: "ERP-1",
|
||||
dosage_unit_code: "MG",
|
||||
dose_time_name: "餐后",
|
||||
frequencyCode: "BID",
|
||||
usageCode: "PO",
|
||||
quantity: 1,
|
||||
|
||||
@ -10,6 +10,7 @@ const { encryptOrderFields, decryptOrderFields, decryptOrderList } = require("./
|
||||
const { getRequiredDrugAccessLevels, doctorMeetsDrugAccessLevels } = require("../drug-access-level");
|
||||
|
||||
const doctorOrPharmacistOfflineMessage = "当前医生或药师不在线,暂无法提交问诊单,请稍后再试";
|
||||
const registerIdPrefix = process.env.CONFIG_CONSULT_REGISTER_ID_PREFIX || '';
|
||||
|
||||
const hisPayStatus = {
|
||||
0: "已支付",
|
||||
@ -158,7 +159,6 @@ async function addConsultOrder(item) {
|
||||
|
||||
async function addOnlineConsultOrder(params) {
|
||||
try {
|
||||
params.registerId = await generateRegisterId(db);
|
||||
const requiredDrugAccessLevels = await getRequiredDrugAccessLevels(db, params.drugs);
|
||||
const storeApi = require("../drug-store");
|
||||
const { success, list, message } = await storeApi(
|
||||
@ -187,6 +187,7 @@ async function addOnlineConsultOrder(params) {
|
||||
params.drugStoreName = store.name;
|
||||
params.drugStoreNo = store.login_no;
|
||||
const doctorCode = params.doctorCode.trim();
|
||||
params.registerId = await generateRegisterId(db);
|
||||
// 指定医生
|
||||
if (doctorCode) {
|
||||
const doctor = await db.collection("hlw-doctor").findOne(
|
||||
@ -261,7 +262,9 @@ async function addOnlineConsultOrder(params) {
|
||||
errorInfo: imAccountResult.errorInfo,
|
||||
};
|
||||
}
|
||||
params.medorg_order_no = common.generateUniqueStr('HLW', 12);
|
||||
const registerId = await generateRegisterId(db);
|
||||
params.registerId = registerId
|
||||
params.medorg_order_no = registerId;
|
||||
// 加密敏感字段
|
||||
const encryptedParams = encryptOrderFields(params);
|
||||
const { insertedId } = await db
|
||||
@ -2378,5 +2381,5 @@ async function generateRegisterId(db, type = 'consult-register-id') {
|
||||
}
|
||||
);
|
||||
const sequence = result.sequence;
|
||||
return `${day}${String(sequence).padStart(4, '0')}`;
|
||||
return `${registerIdPrefix}${day}${String(sequence).padStart(4, '0')}`;
|
||||
}
|
||||
|
||||
@ -130,10 +130,10 @@ exports.formatUploadRxData = function (data, opt) {
|
||||
appr_flag: i.forceSelfPay === 'Y' ? '2' : '1',
|
||||
dose_time_name: i.dose_time_name,
|
||||
}
|
||||
if (item.appr_flag === '2') {
|
||||
//[his要求]当appr_flag=2时必填,如果这个药品维护了限制使用范围的,传1,否则就传2
|
||||
item.self_pay_rea = i.limitUsageScope === 'Y' ? '1' : '2'
|
||||
}
|
||||
// if (item.appr_flag === '2') {
|
||||
// //[his要求]当appr_flag=2时必填,如果这个药品维护了限制使用范围的,传1,否则就传2
|
||||
// item.self_pay_rea = i.limitUsageScope === 'Y' ? '1' : '2'
|
||||
// }
|
||||
return item
|
||||
}),
|
||||
icds: data.diagnosisList.map((i) => ({
|
||||
|
||||
@ -2234,6 +2234,7 @@ async function getYbReviewResult(ctx, db) {
|
||||
name: 1,
|
||||
dosage_form: 1,
|
||||
specification: 1,
|
||||
forceSelfPay: 1
|
||||
},
|
||||
}
|
||||
)
|
||||
@ -2264,6 +2265,7 @@ async function getYbReviewResult(ctx, db) {
|
||||
dosage: drug.dosage,
|
||||
dosage_unit: drug.dosage_unit,
|
||||
dose_time_name: drug.dose_time_name,
|
||||
forceSelfPay: dbDrug.forceSelfPay
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -6,6 +6,8 @@ const { validateApplicableDiagnosisCodes } = require('./applicable-diagnosis');
|
||||
const hnHis = require("../hn-his");
|
||||
let db = "";
|
||||
|
||||
const WithCorpId = process.env.CONFIG_SEARCH_DRUG_WITH_CORPID === 'YES';
|
||||
|
||||
function getConfigOptionNames(list) {
|
||||
return [...new Set((Array.isArray(list) ? list : [])
|
||||
.map(item => typeof item === "string" ? item : item && item.name)
|
||||
@ -67,6 +69,9 @@ async function getDrugInfo(item) {
|
||||
let { name, _id, ids, pinyin_code, keyword, insuranceCodes, barcode } = item;
|
||||
let fuzzyQuery = null;
|
||||
let query = { onSale: true };
|
||||
if (WithCorpId) {
|
||||
query.corpId = item.corpId
|
||||
}
|
||||
if (typeof name === "string") query.name = new RegExp(name.trim(), "i");
|
||||
if (_id) query._id = new ObjectId(_id);
|
||||
if (Array.isArray(ids) && ids.length > 0) {
|
||||
@ -211,6 +216,9 @@ async function getHlwDrugInfo(ctx) {
|
||||
const page = Number.isSafeInteger(requestedPage) && requestedPage > 0 ? requestedPage : 1;
|
||||
const pageSize = Number.isSafeInteger(requestedPageSize) && requestedPageSize > 0 ? requestedPageSize : 10;
|
||||
const query = {};
|
||||
if (WithCorpId) {
|
||||
query.corpId = ctx.corpId
|
||||
}
|
||||
// if (typeof ctx.corpId === "string" && ctx.corpId.trim()) {
|
||||
// query.corpId = ctx.corpId.trim();
|
||||
// }
|
||||
@ -285,6 +293,9 @@ async function addHlwDrugInfo(ctx) {
|
||||
newDrug.tumor_use = 0;
|
||||
}
|
||||
newDrug.createTime = Date.now();
|
||||
if (WithCorpId) {
|
||||
newDrug.corpId = ctx.corpId
|
||||
}
|
||||
const res = await db.collection("drug-info").insertOne(newDrug);
|
||||
if (res && res.insertedId) {
|
||||
updateHisDrugInfo({ id: res.insertedId.toString() })
|
||||
|
||||
@ -23,6 +23,7 @@ const drugFieldDict = [
|
||||
{ name: '抗生素级别', field: 'antibiotic_level', verify: (val, label) => validator.verifyListItem(val, [0, 1, 2, 3], false, label, 0) },
|
||||
{ name: '肿瘤使用', field: 'tumor_use', verify: (val, label) => validator.verifyListItem(val, [0, 1, 2, 3], false, label, 0) },
|
||||
{ name: '风险等级', field: 'riskLevel', verify: (val, label) => validator.verifyListItem(val, ['低风险', '高风险'], false, label) },
|
||||
{ name: '默认医保自费', field: 'forceSelfPay', verify: (val, label) => validator.verifyListItem(val, ['Y', 'N'], false, label) },
|
||||
// 仅在线药品库使用:管理分类(处方药 / OTC)与剂型
|
||||
{ name: '管理分类', field: 'manage_category', verify: (val, label) => validator.verifyString(val, label) },
|
||||
{ name: '剂型', field: 'dosage_form', verify: (val, label) => validator.verifyString(val, label) },
|
||||
|
||||
@ -53,22 +53,55 @@ async function getDiagnosisList(item) {
|
||||
if (category && typeof category === "string") {
|
||||
query.category = new RegExp(category.trim(), "i");
|
||||
}
|
||||
if (typeof keyword == "string" && keyword.trim()) {
|
||||
const normalizedKeyword = typeof keyword === "string" ? keyword.trim() : "";
|
||||
if (normalizedKeyword) {
|
||||
query.$or = [
|
||||
{ name: new RegExp(keyword.trim(), "i") },
|
||||
{ code: new RegExp(keyword.trim(), "i") },
|
||||
{ category: new RegExp(keyword.trim(), "i") },
|
||||
{ pinyin: new RegExp(keyword.trim(), "i") },
|
||||
{ name: new RegExp(normalizedKeyword, "i") },
|
||||
{ code: new RegExp(normalizedKeyword, "i") },
|
||||
{ category: new RegExp(normalizedKeyword, "i") },
|
||||
{ pinyin: new RegExp(normalizedKeyword, "i") },
|
||||
];
|
||||
}
|
||||
const res = await db
|
||||
.collection("hlw-diagnosis")
|
||||
.find(query)
|
||||
.sort({ _id: -1 })
|
||||
.skip((page - 1) * pageSize)
|
||||
.limit(pageSize)
|
||||
.toArray();
|
||||
const total = await db.collection("hlw-diagnosis").countDocuments(query);
|
||||
const collection = db.collection("hlw-diagnosis");
|
||||
let res;
|
||||
if (normalizedKeyword) {
|
||||
// Keep fuzzy search behavior, but bring an exact match for any searchable
|
||||
// field to the front before applying pagination.
|
||||
const exactKeyword = `^${normalizedKeyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`;
|
||||
res = await collection.aggregate([
|
||||
{ $match: query },
|
||||
{
|
||||
$addFields: {
|
||||
exactKeywordMatch: {
|
||||
$cond: [
|
||||
{
|
||||
$or: [
|
||||
{ $regexMatch: { input: { $ifNull: ["$name", ""] }, regex: exactKeyword, options: "i" } },
|
||||
{ $regexMatch: { input: { $ifNull: ["$code", ""] }, regex: exactKeyword, options: "i" } },
|
||||
{ $regexMatch: { input: { $ifNull: ["$category", ""] }, regex: exactKeyword, options: "i" } },
|
||||
{ $regexMatch: { input: { $ifNull: ["$pinyin", ""] }, regex: exactKeyword, options: "i" } },
|
||||
],
|
||||
},
|
||||
1,
|
||||
0,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $sort: { exactKeywordMatch: -1, _id: -1 } },
|
||||
{ $skip: (page - 1) * pageSize },
|
||||
{ $limit: pageSize },
|
||||
{ $project: { exactKeywordMatch: 0 } },
|
||||
]).toArray();
|
||||
} else {
|
||||
res = await collection
|
||||
.find(query)
|
||||
.sort({ _id: -1 })
|
||||
.skip((page - 1) * pageSize)
|
||||
.limit(pageSize)
|
||||
.toArray();
|
||||
}
|
||||
const total = await collection.countDocuments(query);
|
||||
return {
|
||||
success: true,
|
||||
message: "查询成功",
|
||||
|
||||
@ -31,6 +31,18 @@ function validateAccountId(accountId) {
|
||||
return { success: true, accountId: accountId.trim() };
|
||||
}
|
||||
|
||||
function isValidBirthday(birthday) {
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(birthday)) return false;
|
||||
const [year, month, day] = birthday.split('-').map(Number);
|
||||
const date = new Date(Date.UTC(year, month - 1, day));
|
||||
return (
|
||||
date.getUTCFullYear() === year
|
||||
&& date.getUTCMonth() + 1 === month
|
||||
&& date.getUTCDate() === day
|
||||
&& date.getTime() <= Date.now()
|
||||
);
|
||||
}
|
||||
|
||||
function validatePatient(item) {
|
||||
const accountResult = validateAccountId(item && item.accountId);
|
||||
if (!accountResult.success) return accountResult;
|
||||
@ -38,9 +50,28 @@ function validatePatient(item) {
|
||||
const name = typeof item.name === 'string' ? item.name.trim() : '';
|
||||
const certNo = typeof item.certNo === 'string' ? item.certNo.trim() : '';
|
||||
const mobile = typeof item.mobile === 'string' ? item.mobile.trim() : '';
|
||||
let birthday = typeof item.birthday === 'string' ? item.birthday.trim() : '';
|
||||
let sex = typeof item.sex === 'string' ? item.sex.trim() : '';
|
||||
|
||||
if (!name) return { success: false, message: '姓名不能为空' };
|
||||
if (!certNo) return { success: false, message: '证件号不能为空' };
|
||||
let certInfo = null;
|
||||
if (certNo) {
|
||||
certInfo = validator.getChinaIdInfo(certNo);
|
||||
if (!certInfo) return { success: false, message: '证件号格式不正确' };
|
||||
birthday = birthday || certInfo.birthday;
|
||||
sex = sex || certInfo.sex;
|
||||
}
|
||||
if (!birthday) return { success: false, message: '出生日期不能为空' };
|
||||
if (!isValidBirthday(birthday)) {
|
||||
return { success: false, message: '出生日期格式不正确' };
|
||||
}
|
||||
if (!sex) return { success: false, message: '性别不能为空' };
|
||||
if (!['男', '女'].includes(sex)) {
|
||||
return { success: false, message: '性别格式不正确' };
|
||||
}
|
||||
if (certInfo && (birthday !== certInfo.birthday || sex !== certInfo.sex)) {
|
||||
return { success: false, message: '出生日期或性别与证件号信息不一致' };
|
||||
}
|
||||
if (!mobile) return { success: false, message: '手机号不能为空' };
|
||||
if (!validator.isMobile(mobile)) {
|
||||
return { success: false, message: '手机号格式不正确' };
|
||||
@ -60,13 +91,17 @@ function validatePatient(item) {
|
||||
name,
|
||||
certNo,
|
||||
mobile,
|
||||
birthday,
|
||||
sex,
|
||||
address,
|
||||
},
|
||||
data: {
|
||||
accountId: accountResult.accountId,
|
||||
anotherIdNo: Sm4Util.encryptDataForSm3(certNo),
|
||||
anotherIdNo: certNo ? Sm4Util.encryptDataForSm3(certNo) : '',
|
||||
anotherName: Sm4Util.encryptDataForSm3(name),
|
||||
anotherMobile: Sm4Util.encryptDataForSm3(mobile),
|
||||
birthday,
|
||||
sex,
|
||||
address,
|
||||
},
|
||||
};
|
||||
@ -77,24 +112,28 @@ async function addHlwPatient(item, db) {
|
||||
const validation = validatePatient(item);
|
||||
if (!validation.success) return validation;
|
||||
|
||||
const exists = await db.collection(COLLECTION_NAME).countDocuments({
|
||||
accountId: validation.data.accountId,
|
||||
anotherIdNo: validation.data.anotherIdNo,
|
||||
disabled: false,
|
||||
});
|
||||
if (exists > 0) {
|
||||
return { success: false, message: '当前账户下已存在相同证件号的就诊人' };
|
||||
if (validation.origin.certNo) {
|
||||
const exists = await db.collection(COLLECTION_NAME).countDocuments({
|
||||
accountId: validation.data.accountId,
|
||||
anotherIdNo: validation.data.anotherIdNo,
|
||||
disabled: false,
|
||||
});
|
||||
if (exists > 0) {
|
||||
return { success: false, message: '当前账户下已存在相同证件号的就诊人' };
|
||||
}
|
||||
}
|
||||
console.log(validation.data)
|
||||
|
||||
const res = await hnHis({
|
||||
type: 'addHisCustomer',
|
||||
name: validation.origin.name,
|
||||
idCard: validation.origin.certNo,
|
||||
mobile: validation.origin.mobile,
|
||||
address: validation.origin.address,
|
||||
})
|
||||
if (!res.success) return res;
|
||||
// if (validation.origin.certNo) {
|
||||
// const res = await hnHis({
|
||||
// type: 'addHisCustomer',
|
||||
// name: validation.origin.name,
|
||||
// idCard: validation.origin.certNo,
|
||||
// mobile: validation.origin.mobile,
|
||||
// address: validation.origin.address,
|
||||
// birthday: validation.origin.birthday,
|
||||
// sex: validation.origin.sex,
|
||||
// })
|
||||
// if (!res.success) return res;
|
||||
// }
|
||||
|
||||
const result = await db.collection(COLLECTION_NAME).insertOne({
|
||||
...validation.data,
|
||||
@ -117,14 +156,16 @@ async function autoAddHlwPatient(item, db) {
|
||||
const validation = validatePatient(item);
|
||||
if (!validation.success) return validation;
|
||||
|
||||
const exists = await db.collection(COLLECTION_NAME).countDocuments({
|
||||
accountId: validation.data.accountId,
|
||||
anotherIdNo: validation.data.anotherIdNo,
|
||||
disabled: false,
|
||||
});
|
||||
if (validation.origin.certNo) {
|
||||
const exists = await db.collection(COLLECTION_NAME).countDocuments({
|
||||
accountId: validation.data.accountId,
|
||||
anotherIdNo: validation.data.anotherIdNo,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
if (exists > 0) {
|
||||
return { success: true, data: true, message: '就诊人已存在' };
|
||||
if (exists > 0) {
|
||||
return { success: true, data: true, message: '就诊人已存在' };
|
||||
}
|
||||
}
|
||||
|
||||
const result = await addHlwPatient(item, db);
|
||||
@ -144,7 +185,7 @@ async function getHlwPatientList(item, db) {
|
||||
.find({ accountId: validation.accountId, disabled: false })
|
||||
.toArray();
|
||||
|
||||
const data = patients.map(toPatientData);
|
||||
const data = patients.map((patient) => toPatientData(patient, true));
|
||||
|
||||
return { success: true, message: '查询就诊人列表成功', data };
|
||||
} catch (error) {
|
||||
@ -175,13 +216,39 @@ async function getHlwPatient(item, db) {
|
||||
}
|
||||
}
|
||||
|
||||
function toPatientData(patient) {
|
||||
function calculateAge(birthday) {
|
||||
if (!isValidBirthday(birthday)) return '';
|
||||
|
||||
const [birthYear, birthMonth, birthDay] = birthday.split('-').map(Number);
|
||||
const today = new Date();
|
||||
let age = today.getFullYear() - birthYear;
|
||||
const hasHadBirthdayThisYear = (
|
||||
today.getMonth() + 1 > birthMonth
|
||||
|| (today.getMonth() + 1 === birthMonth && today.getDate() >= birthDay)
|
||||
);
|
||||
|
||||
if (!hasHadBirthdayThisYear) age -= 1;
|
||||
return age > 0 ? age : 1;
|
||||
}
|
||||
|
||||
function toPatientData(patient, normalizeDemographics = false) {
|
||||
const certNo = Sm4Util.decryptDataForSm3(patient.anotherIdNo);
|
||||
const certInfo = normalizeDemographics && certNo
|
||||
? validator.getChinaIdInfo(certNo)
|
||||
: null;
|
||||
// 证件号信息仅用于本次响应,不回写数据库中的历史就诊人数据。
|
||||
const birthday = certInfo ? certInfo.birthday : (patient.birthday || '');
|
||||
const sex = certInfo ? certInfo.sex : (patient.sex || '');
|
||||
|
||||
return {
|
||||
id: patient._id.toString(),
|
||||
accountId: patient.accountId,
|
||||
name: Sm4Util.decryptDataForSm3(patient.anotherName),
|
||||
certNo: Sm4Util.decryptDataForSm3(patient.anotherIdNo),
|
||||
certNo,
|
||||
mobile: Sm4Util.decryptDataForSm3(patient.anotherMobile),
|
||||
birthday,
|
||||
sex,
|
||||
...(normalizeDemographics ? { age: calculateAge(birthday) } : {}),
|
||||
address: patient.address || '',
|
||||
createTime: patient.createTime,
|
||||
};
|
||||
|
||||
@ -75,9 +75,13 @@ async function addHisCustomer(item) {
|
||||
const address = typeof item.address === 'string' ? item.address.trim() : '';
|
||||
const psnToken = typeof item.psnToken === 'string' ? item.psnToken.trim() : '';
|
||||
const mdtrt_cert_no = typeof item.mdtrt_cert_no === 'string' ? item.mdtrt_cert_no.trim() : '';
|
||||
const ecToken = typeof item.ecToken === 'string' ? item.ecToken.trim() : '';
|
||||
const card_sn = typeof item.card_sn === 'string' ? item.card_sn.trim() : '';
|
||||
if (!name || !idCard || !mobile) {
|
||||
return { success: false, message: '姓名、证件号、手机号不能为空' }
|
||||
const birthday = typeof item.birthday === 'string' ? item.birthday.trim() : '';
|
||||
const sex = typeof item.sex === 'string' ? item.sex.trim() : '';
|
||||
|
||||
if (!name || !mobile) {
|
||||
return { success: false, message: '姓名、手机号不能为空' }
|
||||
}
|
||||
const payload = {
|
||||
type: 'addHisCustomer',
|
||||
@ -87,21 +91,24 @@ async function addHisCustomer(item) {
|
||||
address,
|
||||
buildType: '1', // 1.自费建档 2.电子凭证建档 3.线下医保
|
||||
};
|
||||
if (!idCard) {
|
||||
payload.brdy = birthday;
|
||||
payload.gend = sex === '男' ? 1 : 2
|
||||
}
|
||||
// 电子凭证建档 医保插件授权建档
|
||||
if (psnToken) {
|
||||
payload.psnToken = psnToken;
|
||||
payload.buildType = "2";
|
||||
// payload.mdtrt_cert_type = '01'
|
||||
}
|
||||
// 线下医保建档
|
||||
// mdtrt_cert_type 就诊凭证类型 01 | 03
|
||||
// mdtrt_cert_no 就诊凭证号码 mdtrt_cert_type为01 填写电子凭证令牌 | mdtrt_cert_type为03 填写社会保障卡卡号
|
||||
if (mdtrt_cert_no) {
|
||||
} else if (mdtrt_cert_no) {
|
||||
payload.mdtrt_cert_type = '03';
|
||||
payload.mdtrt_cert_no = mdtrt_cert_no;
|
||||
payload.card_sn = card_sn; //就诊凭证类型为“03”时填写社会保障卡内码
|
||||
} else if (ecToken) {
|
||||
payload.buildType = '2';
|
||||
payload.mdtrt_cert_type = '01';
|
||||
payload.mdtrt_cert_no = ecToken;
|
||||
}
|
||||
|
||||
const { data, status_code, message } = await request.main(
|
||||
url,
|
||||
payload,
|
||||
|
||||
@ -173,10 +173,32 @@ function isChinaId(s) {
|
||||
return true
|
||||
}
|
||||
|
||||
function getChinaIdInfo(s) {
|
||||
if (typeof s !== 'string' || !isChinaId(s)) return null;
|
||||
|
||||
const sId = idTransfer(s);
|
||||
const birthday = `${sId.substring(6, 10)}-${sId.substring(10, 12)}-${sId.substring(12, 14)}`;
|
||||
const [year, month, day] = birthday.split('-').map(Number);
|
||||
const date = new Date(Date.UTC(year, month - 1, day));
|
||||
if (
|
||||
date.getUTCFullYear() !== year
|
||||
|| date.getUTCMonth() + 1 !== month
|
||||
|| date.getUTCDate() !== day
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
birthday,
|
||||
sex: Number(sId.charAt(16)) % 2 === 0 ? '女' : '男',
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
verifyString,
|
||||
verifyNumber,
|
||||
verifyListItem,
|
||||
isMobile,
|
||||
isChinaId
|
||||
}
|
||||
isChinaId,
|
||||
getChinaIdInfo,
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user