package open.dolphin.ejb;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Remote;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import open.dolphin.dto.PatientSearchSpec;
import open.dolphin.infomodel.DocumentModel;
import open.dolphin.infomodel.HealthInsuranceModel;
import open.dolphin.infomodel.KarteState;
import open.dolphin.infomodel.PatientModel;
import open.dolphin.infomodel.PatientVisitModel;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.jboss.ejb3.annotation.SecurityDomain;
@Stateless
@SecurityDomain("openDolphin")
@RolesAllowed("user")
@Remote({RemotePatientService.class})
public class RemotePatientServiceImpl extends DolphinService implements RemotePatientService {
private static final long serialVersionUID = -456476244129106722L;
private Logger logger = Logger.getLogger("CONSOLE");
@Resource
private SessionContext ctx;
@PersistenceContext
private EntityManager em;
/**
* 患者オブジェクトを取得する。
* @param spec PatientSearchSpec 検索仕様
* @return 患者オブジェクトの Collection
*/
@SuppressWarnings("unchecked")
@Override
public List<PatientModel> getPatients(PatientSearchSpec spec) {
String fid = getCallersFacilityId(ctx);
// 戻り値
List<PatientModel> ret = new ArrayList<PatientModel>();
// 絞り込み id
List<Long>ids = spec.getNarrowingList();
switch (spec.getCode()) {
case PatientSearchSpec.DATE_SEARCH:
final String dateQuery = "from PatientVisitModel p where p.facilityId = :fid and p.pvtDate like :date";
final String dateQueryNarrow = dateQuery + " and p.patient.id in (:ids)";
List<PatientVisitModel> pvtList;
if (ids.isEmpty()) {
pvtList = em.createQuery(dateQuery).setParameter("fid", fid).setParameter("date", spec.getDate()+ "%")
.getResultList();
} else {
pvtList = em.createQuery(dateQueryNarrow).setParameter("fid", fid).setParameter("date", spec.getDate()+ "%")
.setParameter("ids", ids).getResultList();
}
for (PatientVisitModel pvt : pvtList) {
ret.add(pvt.getPatient());
}
break;
case PatientSearchSpec.ID_SEARCH:
final String idQuery = "from PatientModel p where p.facilityId = :fid and p.patientId like :pid";
final String idQueryNarrow = idQuery + " and p.id in (:ids)";
String pid = spec.getPatientId();
if (!pid.endsWith("%")) pid +="%";
if (ids.isEmpty()) {
ret = em.createQuery(idQuery).setMaxResults(50).setParameter("fid", fid).setParameter("pid", pid)
.getResultList();
} else {
ret = em.createQuery(idQueryNarrow).setMaxResults(50).setParameter("fid", fid).setParameter("pid", pid)
.setParameter("ids", ids).getResultList();
}
break;
case PatientSearchSpec.NAME_SEARCH:
case PatientSearchSpec.KANA_SEARCH:
case PatientSearchSpec.ROMAN_SEARCH:
final String nameQuery = "from PatientModel p where p.facilityId = :fid and "
+ "(p.fullName like :name or p.kanaName like :name or p.romanName like :name)";
final String nameQueryNarrow = nameQuery + " and p.id in (:ids)";
String name = spec.getName();
if (!name.endsWith("%")) name +="%";
if (ids.isEmpty()) {
ret = em.createQuery(nameQuery).setParameter("fid", fid).setParameter("name", name)
.getResultList();
} else {
ret = em.createQuery(nameQueryNarrow).setParameter("fid", fid).setParameter("name", name)
.setParameter("ids", ids).getResultList();
}
break;
case PatientSearchSpec.TELEPHONE_SEARCH:
final String telQuery = "from PatientModel p where p.facilityId = :fid and (p.telephone like :number or p.mobilePhone like :number)";
final String telQueryNarrow = telQuery + " and p.id in (:ids)";
String number = spec.getTelephone();
if (!number.endsWith("%")) number += "%";
if (ids.isEmpty()) {
ret = em.createQuery(telQuery).setParameter("fid", fid).setParameter("number", number)
.getResultList();
} else {
ret = em.createQuery(telQueryNarrow).setParameter("fid", fid).setParameter("number", number)
.setParameter("ids", ids).getResultList();
}
break;
case PatientSearchSpec.ZIPCODE_SEARCH:
final String zipQuery = "from PatientModel p where p.facilityId = :fid and p.address.zipCode like :zipCode";
final String zipQueryNarrow = zipQuery + " and p.id in (:ids)";
String zipCode = spec.getZipCode();
if (!zipCode.endsWith("%")) zipCode += "%";
if (ids.isEmpty()) {
ret = em.createQuery(zipQuery).setParameter("fid", fid).setParameter("zipCode", zipCode)
.getResultList();
} else {
ret = em.createQuery(zipQueryNarrow).setParameter("fid", fid).setParameter("zipCode", zipCode)
.setParameter("ids", ids).getResultList();
}
break;
case PatientSearchSpec.ADDRESS_SEARCH:
final String addressQuery = "from PatientModel p where p.facilityId = :fid and p.address.address like :address";
final String addressQueryNarrow = addressQuery + " and p.id in (:ids)";
String address = spec.getAddress();
if (!address.endsWith("%")) address += "%";
if (ids.isEmpty()) {
ret = em.createQuery(addressQuery).setParameter("fid", fid).setParameter("address", address)
.getResultList();
} else {
ret = em.createQuery(addressQueryNarrow).setParameter("fid", fid).setParameter("address", address)
.setParameter("ids", ids).getResultList();
}
break;
case PatientSearchSpec.EMAIL_SEARCH:
final String emailQuery = "from PatientModel p where p.facilityId = :fid and p.email like :address";
final String emailQueryNarrow = emailQuery + " and p.id in (:ids)";
address = spec.getEmail();
if (!address.endsWith("%")) address += "%";
if (ids.isEmpty()) {
ret = em.createQuery(emailQuery).setParameter("fid", fid).setParameter("email", address)
.getResultList();
} else {
ret = em.createQuery(emailQueryNarrow).setParameter("fid", fid).setParameter("email", address)
.setParameter("ids", ids).getResultList();
}
break;
case PatientSearchSpec.BIRTHDAY_SEARCH:
final String birthdayQuery = "from PatientModel p where p.facilityId = :fid and p.birthday like :birthday";
final String birthdayQueryNarrow = birthdayQuery + " and p.id in (:ids)";
String birthday = spec.getBirthday();
if (!birthday.endsWith("%")) birthday += "%";
if (ids.isEmpty()) {
ret = em.createQuery(birthdayQuery).setMaxResults(50).setParameter("fid", fid).setParameter("birthday", birthday)
.getResultList();
} else {
ret = em.createQuery(birthdayQueryNarrow).setMaxResults(50).setParameter("fid", fid).setParameter("birthday", birthday)
.setParameter("ids", ids).getResultList();
}
break;
case PatientSearchSpec.MEMO_SEARCH:
final String memoQuery = "select p.karte.patient from PatientMemoModel p where p.karte.patient.facilityId = :fid and p.memo like :memo";
final String memoQueryNarrow = memoQuery + " and p.karte.patient.id in (:ids)";
String memo = spec.getSearchText();
if (!memo.endsWith("%")) memo += "%";
if (ids.isEmpty()) {
ret = em.createQuery(memoQuery).setParameter("fid", fid).setParameter("memo", memo)
.getResultList();
} else {
ret = em.createQuery(memoQueryNarrow).setParameter("fid", fid).setParameter("memo", memo)
.setParameter("ids", ids).getResultList();
}
break;
case PatientSearchSpec.FULL_TEXT_SEARCH:
final FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
final Analyzer analyzer = fullTextEntityManager.getSearchFactory().getAnalyzer(DocumentModel.class);
final org.apache.lucene.util.Version ver = org.apache.lucene.util.Version.LUCENE_35;
final String pk = "karte.patient.id:";
// preparing seach text with narrowing list
String searchText = spec.getSearchText();
if (! ids.isEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append(pk).append(String.valueOf(ids.get(0)));
for (int i=1; i<ids.size(); i++) {
sb.append(" OR ").append(pk).append(String.valueOf(ids.get(i)));
}
searchText = String.format("(%s) AND (%s)", spec.getSearchText(), sb.toString());
}
try {
// create native Lucene query
org.apache.lucene.queryParser.QueryParser parser = new QueryParser(ver, "modules.beanBytes", analyzer);
parser.setAutoGeneratePhraseQueries(true); // http://lucene.jugem.jp/?eid=403
org.apache.lucene.search.Query luceneQuery = parser.parse(searchText);
// wrap Lucene query in a javax.persistence.Query
javax.persistence.Query persistenceQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, DocumentModel.class);
// Too many results (>32768) cause
// java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: xxxxx
// 暫定的に 1000 にしておく
persistenceQuery.setMaxResults(1000);
// execute search
List<DocumentModel> result = persistenceQuery.getResultList();
for (DocumentModel dm : result) {
PatientModel pm = dm.getKarte().getPatient();
if (!ret.contains(pm)) {
ret.add(pm);
}
}
} catch (ParseException e) {
logger.info(e.getMessage(), e.getCause());
}
break;
}
if (! ret.isEmpty()) {
// pvt をまとめて取得する(高速化のため)
List<PatientVisitModel> pvts = em.createQuery("from PatientVisitModel p "
+ "where p.facilityId = :fid and p.status != :status and (p.patient in (:pts)) order by p.pvtDate desc")
.setParameter("fid", fid)
.setParameter("pts", ret)
.setParameter("status", KarteState.CANCEL_PVT)
.getResultList();
// まとめて取った pvt から最新の日付を PatientModel にセット
for (PatientModel pm : ret) {
for (PatientVisitModel pvt : pvts) {
// 最初にマッチした pvt が最新 (last visit)
if (pm.getId() == pvt.getPatient().getId()) {
pm.setLastVisit(pvt.getPvtDate());
break;
}
}
}
}
return ret;
}
/**
* HealthInsurance を返す
* @param patientPk
* @return
*/
@Override
public List<HealthInsuranceModel> getHealthInsurance(long patientPk) {
final String sql = "from HealthInsuranceModel h where h.patient.id = :pk";
List<HealthInsuranceModel> insurances = em.createQuery(sql).setParameter("pk", patientPk).getResultList();
return insurances;
}
/**
* 患者ID(BUSINESS KEY)を指定して患者オブジェクトを返す。
*
* @param patientId 施設内患者ID
* @return 該当するPatientModel
*/
@SuppressWarnings({"unchecked","unchecked"})
@Override
public PatientModel getPatient(String patientId) {
String facilityId = getCallersFacilityId(ctx);
// 患者レコードは FacilityId と patientId で複合キーになっている
PatientModel bean
= (PatientModel)em.createQuery("from PatientModel p where p.facilityId = :fid and p.patientId = :pid")
.setParameter("fid", facilityId)
.setParameter("pid", patientId)
.getSingleResult();
long pk = bean.getId();
// Lazy Fetch の 基本属性を検索する
// 患者の健康保険を取得する
Collection insurances
= em.createQuery("from HealthInsuranceModel h where h.patient.id = :pk")
.setParameter("pk", pk).getResultList();
bean.setHealthInsurances(insurances);
return bean;
}
/**
* 患者を登録する。
* @param patient PatientModel
* @return データベース Primary Key
*/
@Override
public long addPatient(PatientModel patient) {
String facilityId = getCallersFacilityId(ctx);
patient.setFacilityId(facilityId);
em.persist(patient);
long pk = patient.getId();
return pk;
}
/**
* 患者情報を更新する。
* @param patient 更新する患者
* @return 更新数
*/
@Override
public int update(PatientModel patient) {
em.merge(patient);
return 1;
}
}