说明
Epic是全美最大电子病历供应商。
FHIR (Fast Health Interoperability Resources)旨在实现医疗健康信息的交换。其中包括了临床数据和健康相关的行政管理数据、公共卫生和临床研究数据。既包括了人类和兽医,又适合大多数应用场景——住院、慢病管理、社区护理、健康联盟等。
在https://fhir.epic.com/ 中有详细的文档
这里有详细的注册测试账号和沙箱使用的说明。
生成token
调用get_token函数生成token。
public static string make_jwt(Option option)
{
Dictionary<string, object> h = new Dictionary<string, object>();
h["alg"] = "RS384";
h["typ"] = "JWT";
h["kid"] = option.kid ;
string header = Newtonsoft.Json.JsonConvert.SerializeObject(h);
DateTime d1 = DateTime.UtcNow.AddSeconds(60 * 4);
//1970-01-01T00:00:00Z UTC)
DateTime d0 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
int exp = ((int)(d1 - d0).TotalSeconds);
Dictionary<string, object> p = new Dictionary<string, object>();
p["iss"] = option.client_id;
p["sub"] = option.client_id;
p["aud"] = option.oauth_url;
p["jti"] = Guid.NewGuid().ToString("N");
p["exp"] = exp;
string payload = Newtonsoft.Json.JsonConvert.SerializeObject(p);
//signature = RSA-SHA384(base64urlEncoding(header) + '.' + base64urlEncoding(payload), privatekey)
byte[] signature_buff = RSASha384Signature.SignData(Base64UrlEncoder.base64urlEncoding(header) + '.' + Base64UrlEncoder.base64urlEncoding(payload), option.privatekey_pem);
string jwt = Base64UrlEncoder.base64urlEncoding(header) + '.' + Base64UrlEncoder.base64urlEncoding(payload) + '.' + Base64UrlEncoder.Encode(signature_buff);
return jwt;
}
public static string get_token(Option option)
{
string jwt = make_jwt(option);
Dictionary<string, string> postParameters = new Dictionary<string, string>();
postParameters["grant_type"] = "client_credentials";
postParameters["client_assertion_type"] = "urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer";
postParameters["client_assertion"] = jwt;
Dictionary<string, string> Headers = new Dictionary<string, string>();
StringBuilder paraStrBuilder = new StringBuilder();
foreach (string key in postParameters.Keys)
{
paraStrBuilder.AppendFormat("{0}={1}&", key, postParameters[key]);
}
string para = paraStrBuilder.ToString();
if (para.EndsWith("&"))
para = para.Remove(para.Length - 1, 1);
string result;
HttpUtils.HttpPostBuff(option.oauth_url, Headers, para, out result, option.proxy_url, option.proxy_port);
return result;
}
使用了RSASha384
public class RSASha384Signature
{
public static byte[] SignData(string data, string privatekey_pem)
{
using (var rsa = new RSACryptoServiceProvider())
{
RsaPrivateCrtKeyParameters privateKeyParams = ReadPrivateKey(privatekey_pem);
RSAParameters rsaParams = new RSAParameters();
rsaParams.Modulus = privateKeyParams.Modulus.ToByteArrayUnsigned();
rsaParams.Exponent = privateKeyParams.PublicExponent.ToByteArrayUnsigned();
rsaParams.D = privateKeyParams.Exponent.ToByteArrayUnsigned();
rsaParams.P = privateKeyParams.P.ToByteArrayUnsigned();
rsaParams.Q = privateKeyParams.Q.ToByteArrayUnsigned();
rsaParams.DP = privateKeyParams.DP.ToByteArrayUnsigned();
rsaParams.DQ = privateKeyParams.DQ.ToByteArrayUnsigned();
rsaParams.InverseQ = privateKeyParams.QInv.ToByteArrayUnsigned();
rsa.ImportParameters(rsaParams);
// 使用SHA384进行签名
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
byte[] signature = rsa.SignData(dataBytes, CryptoConfig.MapNameToOID("SHA384"));
return signature;
}
}
public static RsaPrivateCrtKeyParameters ReadPrivateKey(string privatekey_pem)
{
using (TextReader reader = new StringReader(privatekey_pem))
{
var pemReader = new PemReader(reader);
var keyPair = pemReader.ReadObject() as RsaPrivateCrtKeyParameters;
if (keyPair != null)
{
return keyPair;
}
throw new InvalidDataException("Private key not found in file.");
}
}
}
查询病人
查询病人可以使用 Hl7.Fhir.Rest (主要EPIC的有些接口 使用Hl7.Fhir.Rest 会报错,主要是日期格式)
try
{
FhirClient client = Pub.get_FhirClient();
SearchParams param = new SearchParams();
if (textBox_given.Text.Trim() != "")
param.Add("given", textBox_given.Text.Trim());
if (textBox_identifier.Text.Trim() != "")
param.Add("identifier", textBox_identifier.Text.Trim());
if (textBox_family.Text.Trim() != "")
param.Add("family", textBox_family.Text.Trim());
param.Count = 10;
Bundle b = client.Search<Patient>(param);
display_patient(b);
}
catch (Exception err)
{
textBox_result.Text = err.Message;
}
病人数据
search_by_patient("Appointment");
search_by_patient("Condition");
search_by_patient("Coverage");
search_by_patient("DiagnosticReport");
search_by_patient("DocumentReference");
search_by_patient("Encounter");
search_by_patient("Immunization");
search_by_patient("InsurancePlan");
search_by_patient("MedicationRequest");
search_by_patient("MedicationStatement");
search_by_patient("Observation");
search_by_patient("Procedure");
search_by_patient("QuestionnaireResponse");
search_by_patient("ServiceRequest");
search_by_patient("Slot");
private void search_by_patient(string type)
{
if (PatientId == "")
{
MessageBox.Show("Please select patient");
return;
}
try
{
// Pub.get_FhirClient().Get(type + "?patient=" + PatientId);
string json = Pub.get_RestClient(type + "?patient=" + PatientId);
json = Pub.check_date(json);
FhirJsonParser fp = new FhirJsonParser();
Hl7.Fhir.Model.Bundle b = fp.Parse<Hl7.Fhir.Model.Bundle>(json);
Form_List frm = new Form_List();
frm.show_list(b);
frm.ShowDialog();
frm.Dispose();
}
catch (Exception err)
{
textBox_result.Text = err.Message;
}
}