package open.dolphin.ejb;
import java.util.*;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import open.dolphin.dto.*;
import open.dolphin.exception.CanNotDeleteException;
import open.dolphin.infomodel.*;
import org.apache.log4j.Logger;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.jboss.ejb3.annotation.SecurityDomain;
@Stateless
@SecurityDomain("openDolphin")
@RolesAllowed("user")
@Remote({RemoteKarteService.class})
public class RemoteKarteServiceImpl extends DolphinService implements RemoteKarteService {
private static final long serialVersionUID = 1L;
private Logger logger = Logger.getLogger("CONSOLE");
@PersistenceContext
private EntityManager em;
/**
* カルテの基礎的な情報をまとめて返す。
* @param patientPk 患者の Database Primary Key
* @param fromDate 各種エントリの検索開始日
* @return 基礎的な情報をフェッチした KarteBean
*/
@Override
public KarteBean getKarte(long patientPk, Date fromDate) {
try {
// 最初に患者のカルテを取得する
KarteBean karte = (KarteBean) em.createQuery("from KarteBean k where k.patient.id = :patientPk")
.setParameter("patientPk", patientPk)
.getSingleResult();
// カルテの PK を得る
long karteId = karte.getId();
// アレルギーデータを取得する
List observations = em.createQuery("from ObservationModel o where o.karte.id=:karteId and o.observation='Allergy'")
.setParameter("karteId", karteId)
.getResultList();
if (observations != null && observations.size() > 0) {
List<AllergyModel> allergies = new ArrayList<AllergyModel>(observations.size());
for (Iterator iter = observations.iterator(); iter.hasNext(); ) {
ObservationModel observation = (ObservationModel) iter.next();
AllergyModel allergy = new AllergyModel();
allergy.setObservationId(observation.getId());
allergy.setFactor(observation.getPhenomenon());
allergy.setSeverity(observation.getCategoryValue());
allergy.setIdentifiedDate(observation.confirmDateAsString());
allergies.add(allergy);
}
karte.addEntryCollection("allergy", allergies);
}
// 身長データを取得する
observations = em.createQuery("from ObservationModel o where o.karte.id=:karteId and o.observation='PhysicalExam' and o.phenomenon='bodyHeight'")
.setParameter("karteId", karteId)
.getResultList();
if (observations != null && observations.size() > 0) {
List<PhysicalModel> physicals = new ArrayList<PhysicalModel>(observations.size());
for (Iterator iter = observations.iterator(); iter.hasNext(); ) {
ObservationModel observation = (ObservationModel) iter.next();
PhysicalModel physical = new PhysicalModel();
physical.setHeightId(observation.getId());
physical.setHeight(observation.getValue());
physical.setIdentifiedDate(observation.confirmDateAsString());
physical.setMemo(ModelUtils.getDateAsString(observation.getRecorded()));
physicals.add(physical);
}
karte.addEntryCollection("height", physicals);
}
// 体重データを取得する
observations = em.createQuery("from ObservationModel o where o.karte.id=:karteId and o.observation='PhysicalExam' and o.phenomenon='bodyWeight'")
.setParameter("karteId", karteId)
.getResultList();
if (observations != null && observations.size() > 0) {
List<PhysicalModel> physicals = new ArrayList<PhysicalModel>(observations.size());
for (Iterator iter = observations.iterator(); iter.hasNext(); ) {
ObservationModel observation = (ObservationModel) iter.next();
PhysicalModel physical = new PhysicalModel();
physical.setWeightId(observation.getId());
physical.setWeight(observation.getValue());
physical.setIdentifiedDate(observation.confirmDateAsString());
physical.setMemo(ModelUtils.getDateAsString(observation.getRecorded()));
physicals.add(physical);
}
karte.addEntryCollection("weight", physicals);
}
// 直近の来院日エントリーを取得しカルテに設定する
List latestVisits = em.createQuery("from PatientVisitModel p where p.patient.id = :patientPk and p.pvtDate >= :fromDate")
.setParameter("patientPk", patientPk)
.setParameter("fromDate", ModelUtils.getDateAsString(fromDate))
.getResultList();
if (latestVisits != null && latestVisits.size() > 0) {
List<String> visits = new ArrayList<String>();
for (Iterator iter=latestVisits.iterator(); iter.hasNext() ;) {
PatientVisitModel bean = (PatientVisitModel) iter.next();
// キャンセルされた受付は無視する
if (bean.getState() != KarteState.CANCEL_PVT) {
visits.add(bean.getPvtDate());
}
}
karte.addEntryCollection("visit", visits);
}
// 文書履歴エントリーを取得しカルテに設定する
List documents = em.createQuery("from DocumentModel d where d.karte.id = :karteId and d.started >= :fromDate and (d.status='F' or d.status='T')")
.setParameter("karteId", karteId)
.setParameter("fromDate", fromDate)
.getResultList();
if (documents != null && documents.size() > 0) {
List<DocInfoModel> c = new ArrayList<DocInfoModel>();
for (Iterator iter = documents.iterator(); iter.hasNext() ;) {
DocumentModel docBean = (DocumentModel) iter.next();
docBean.toDetuch();
c.add(docBean.getDocInfo());
}
karte.addEntryCollection("docInfo", c);
}
// 患者Memoを取得する
List memo = em.createQuery("from PatientMemoModel p where p.karte.id = :karteId")
.setParameter("karteId", karteId)
.getResultList();
if (memo != null && memo.size() >0) {
karte.addEntryCollection("patientMemo", memo);
}
return karte;
} catch (NoResultException e) {
// 患者登録の際にカルテも生成してある
logger.info(e.getMessage(), e.getCause());
}
return null;
}
/**
* 文書履歴エントリを取得する。
* @param karteId カルテId
* @param fromDate 取得開始日
* @param status ステータス
* @return DocInfo のコレクション
*/
@Override
public List getDocumentList(DocumentSearchSpec spec) {
List documents;
if (spec.isIncludeModifid()) {
documents = em.createQuery("from DocumentModel d where d.karte.id = :karteId and d.started >= :fromDate and d.status !='D'")
.setParameter("karteId", spec.getKarteId())
.setParameter("fromDate", spec.getFromDate())
.getResultList();
} else {
documents = em.createQuery("from DocumentModel d where d.karte.id = :karteId and d.started >= :fromDate and (d.status='F' or d.status='T')")
.setParameter("karteId", spec.getKarteId())
.setParameter("fromDate", spec.getFromDate())
.getResultList();
}
List<DocInfoModel> result = new ArrayList<DocInfoModel>();
for (Iterator iter = documents.iterator(); iter.hasNext() ;) {
DocumentModel docBean = (DocumentModel) iter.next();
// モデルからDocInfo へ必要なデータを移す
// クライアントが DocInfo だけを利用するケースがあるため
docBean.toDetuch();
result.add(docBean.getDocInfo());
}
return result;
}
/**
* 文書(DocumentModel Object)を取得する。
* HashMap 使用で 1950msec -> 1517msec になった。残りはほぼ query の時間
* @param ids DocumentModel の pkコレクション
* @return DocumentModelのコレクション
*/
@Override
public List<DocumentModel> getDocuments(List<Long> ids) {
//long t = System.currentTimeMillis();
List<DocumentModel> ret = new ArrayList<DocumentModel>();
// まとめて query
List<ModuleModel> mods = em.createQuery("from ModuleModel m where m.document.id in (:ids)")
.setParameter("ids", ids)
.getResultList();
List<SchemaModel> imgs = em.createQuery("from SchemaModel m where m.document.id in (:ids)")
.setParameter("ids", ids)
.getResultList();
// とってきた ModuleModel を id 毎に分ける
HashMap<Long, List<ModuleModel>> modsMap = new HashMap<Long, List<ModuleModel>>();
for (ModuleModel m : mods) {
Long id = m.getDocument().getId();
List<ModuleModel> list = modsMap.get(id);
if (list == null) {
list = new ArrayList<ModuleModel>();
modsMap.put(id, list);
}
list.add(m);
}
// とってきた SchemaModel を id 毎に分ける
HashMap<Long, List<SchemaModel>> imgsMap = new HashMap<Long, List<SchemaModel>>();
for (SchemaModel m : imgs) {
Long id = m.getDocument().getId();
List<SchemaModel> list = imgsMap.get(id);
if (list == null) {
list = new ArrayList<SchemaModel>();
imgsMap.put(id, list);
}
list.add(m);
}
// とってきた list を DocumentModel に分配
for (Long id : ids) {
// DocuentBean を取得する
DocumentModel document = em.find(DocumentModel.class, id);
// ModuleBean を取得する
List<ModuleModel> modules = modsMap.get(id);
if (modules == null) { modules = new ArrayList<ModuleModel>(); }
document.setModules(modules);
// SchemaModel を取得する
List<SchemaModel> images = imgsMap.get(id);
if (images == null) { images = new ArrayList<SchemaModel>(); }
document.setSchema(images);
ret.add(document);
}
//System.out.println("---- lap= " + (System.currentTimeMillis() - t));
return ret;
}
/**
* ドキュメント DocumentModel オブジェクトを保存する。
* @param karteId カルテId
* @param document 追加するDocumentModel オブジェクト
* @return 追加した数
*/
@Override
public long addDocument(DocumentModel document) {
// 永続化する
em.persist(document);
// ID
long id = document.getId();
// 修正版の処理を行う
DocInfoModel docInfo = document.getDocInfo();
long parentPk = document.getDocInfo().getParentPk();
// 親がないならリターン
if (parentPk == 0L) {
return id;
}
DocumentModel old = em.find(DocumentModel.class, parentPk);
if (old == null) {
return id;
}
// 親文書が仮保存文書なら残す必要なし。なぜならそれは仮保存だから。by masuda-sensei
if (IInfoModel.STATUS_TMP.equals(old.getStatus())) {
// 編集元文書の情報を引き継ぐ
DocInfoModel pInfo = old.getDocInfo();
document.setLinkId(old.getLinkId());
document.setLinkRelation(old.getLinkRelation());
//docInfo.setParentPk(pInfo.getParentPk()); // parentPk = linkId
docInfo.setParentId(pInfo.getParentId());
docInfo.setParentIdRelation(pInfo.getParentIdRelation());
docInfo.setVersionNumber(pInfo.getVersionNumber());
// 編集元は削除
em.remove(old);
} else {
// 適合終了日を新しい版の確定日にする
Date ended = document.getConfirmed();
// オリジナルを取得し 終了日と status = M を設定する
old.setEnded(ended);
old.setStatus(STATUS_MODIFIED);
// HibernateSearchのFulTextEntityManagerを用意。修正済みのものはインデックスから削除する by masuda-sensei
final FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
fullTextEntityManager.purge(DocumentModel.class, parentPk);
// 関連するモジュールとイメージに同じ処理を実行する
Collection oldModules = em.createQuery("from ModuleModel m where m.document.id = :id")
.setParameter("id", parentPk).getResultList();
for (Iterator iter = oldModules.iterator(); iter.hasNext(); ) {
ModuleModel model = (ModuleModel)iter.next();
model.setEnded(ended);
model.setStatus(STATUS_MODIFIED);
}
Collection oldImages = em.createQuery("from SchemaModel s where s.document.id = :id")
.setParameter("id", parentPk).getResultList();
for (Iterator iter = oldImages.iterator(); iter.hasNext(); ) {
SchemaModel model = (SchemaModel)iter.next();
model.setEnded(ended);
model.setStatus(STATUS_MODIFIED);
}
}
return id;
}
/**
* ドキュメントを論理削除する。
* @param id 論理削除するドキュメントの primary key
* @return 削除した件数
*/
@Override
public int deleteDocument(long id) {
//オリジナルでは修正したり仮保存をした文書を削除できないので改変 by masuda-sensei
// 対象 Document を取得する
DocumentModel target = em.find(DocumentModel.class, id);
// その親文書を取得
DocumentModel parent = getParent(target);
// 関連するDocumentModelを再帰で取得する
Set<DocumentModel> delSet = getChildren(parent);
Date ended = new Date();
for (DocumentModel delete : delSet) {
long delId = delete.getId();
// HibernateSearchのFulTextEntityManagerを用意。削除済みのものはインデックスから削除する
final FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
fullTextEntityManager.purge(DocumentModel.class, delId);
if (IInfoModel.STATUS_TMP.equals(delete.getStatus())) {
// 仮文書の場合は抹消スル
DocumentModel dm = em.find(DocumentModel.class, delId);
em.remove(dm);
} else {
// 削除フラグをたてる
delete.setStatus(IInfoModel.STATUS_DELETE);
delete.setEnded(ended);
// 関連するモジュールに同じ処理を行う
List<ModuleModel> deleteModules =
em.createQuery("from ModuleModel m where m.document.id=:id")
.setParameter("id", delId)
.getResultList();
for (ModuleModel model : deleteModules) {
model.setStatus(IInfoModel.STATUS_DELETE);
model.setEnded(ended);
}
// 関連する画像に同じ処理を行う
List<SchemaModel> deleteImages =
em.createQuery("from SchemaModel i where i.document.id=:id")
.setParameter("id", delId)
.getResultList();
for (SchemaModel model : deleteImages) {
model.setStatus(IInfoModel.STATUS_DELETE);
model.setEnded(ended);
}
}
}
return 1;
}
/**
* 親文書を追いかける by masuda-sensei
* @param dm
* @return
*/
public DocumentModel getParent(DocumentModel dm) {
long linkId = dm.getLinkId();
DocumentModel model = dm;
while (linkId != 0) {
model = em.find(DocumentModel.class, linkId);
linkId = model.getLinkId();
}
return model;
}
/**
* 子文書を再帰で探す by masuda-sensei
* @param parent
* @return
*/
public Set<DocumentModel> getChildren(DocumentModel parent) {
Set<DocumentModel> ret = new HashSet<DocumentModel>();
// 親を追加
ret.add(parent);
List<DocumentModel> children =
em.createQuery("from DocumentModel d where d.linkId=:id")
.setParameter("id", parent.getId())
.getResultList();
// 子供の子供をリストに追加
for (DocumentModel child : children) {
ret.addAll(getChildren(child));
}
return ret;
}
/**
* ドキュメントのタイトルを変更する。
* @param pk 変更するドキュメントの primary key
* @return 変更した件数
*/
@Override
public int updateTitle(long pk, String title) {
DocumentModel update = em.find(DocumentModel.class, pk);
update.getDocInfo().setTitle(title);
return 1;
}
/**
* ModuleModelエントリを取得する。
* @param spec モジュール検索仕様
* @return ModuleModelリストのリスト
*/
@Override
public List<List> getModules(ModuleSearchSpec spec) {
// 抽出期間は別けられている
Date[] fromDate = spec.getFromDate();
Date[] toDate = spec.getToDate();
int len = fromDate.length;
List<List> ret = new ArrayList<List>(len);
// 抽出期間セットの数だけ繰り返す
for (int i = 0; i < len; i++) {
List modules
= em.createQuery("from ModuleModel m where m.karte.id = :karteId and m.moduleInfo.entity = :entity and m.started between :fromDate and :toDate and m.status='F'")
.setParameter("karteId", spec.getKarteId())
.setParameter("entity", spec.getEntity())
.setParameter("fromDate", fromDate[i])
.setParameter("toDate", toDate[i])
.getResultList();
ret.add(modules);
}
return ret;
}
/**
* SchemaModelエントリを取得する。
* @param karteId カルテID
* @param fromDate
* @param toDate
* @return SchemaModelエントリの配列
*/
@Override
public List<List> getImages(ImageSearchSpec spec) {
// 抽出期間は別けられている
Date[] fromDate = spec.getFromDate();
Date[] toDate = spec.getToDate();
int len = fromDate.length;
List<List> ret = new ArrayList<List>(len);
// 抽出期間セットの数だけ繰り返す
for (int i = 0; i < len; i++) {
List modules
= em.createQuery("from SchemaModel i where i.karte.id = :karteId and i.started between :fromDate and :toDate and i.status='F'")
.setParameter("karteId", spec.getKarteId())
.setParameter("fromDate", fromDate[i])
.setParameter("toDate", toDate[i])
.getResultList();
ret.add(modules);
}
return ret;
}
/**
* 画像を取得する。
* @param id SchemaModel Id
* @return SchemaModel
*/
@Override
public SchemaModel getImage(long id) {
SchemaModel image = em.find(SchemaModel.class, id);
return image;
}
/**
* 傷病名リストを取得する。
* @param spec 検索仕様
* @return 傷病名のリスト
*/
@Override
public List<RegisteredDiagnosisModel> getDiagnosis(DiagnosisSearchSpec spec) {
List ret;
// 疾患開始日を指定している
if (spec.getFromDate() != null) {
ret = em.createQuery("from RegisteredDiagnosisModel r where r.karte.id = :karteId and r.started >= :fromDate")
.setParameter("karteId", spec.getKarteId())
.setParameter("fromDate", spec.getFromDate())
.getResultList();
} else {
// 全期間の傷病名を得る
ret = em.createQuery("from RegisteredDiagnosisModel r where r.karte.id = :karteId")
.setParameter("karteId", spec.getKarteId())
.getResultList();
}
return ret;
}
/**
* 傷病名を追加する。
* @param addList 追加する傷病名のリスト
* @return idのリスト
*/
@Override
public List<Long> addDiagnosis(List<RegisteredDiagnosisModel> addList) {
List<Long> ret = new ArrayList<Long>(addList.size());
for (RegisteredDiagnosisModel bean : addList) {
em.persist(bean);
ret.add(Long.valueOf(bean.getId()));
}
return ret;
}
/**
* 傷病名を更新する。
* @param updateList
* @return 更新数
*/
@Override
public int updateDiagnosis(List<RegisteredDiagnosisModel> updateList) {
int cnt = 0;
for (RegisteredDiagnosisModel bean : updateList) {
em.merge(bean);
cnt++;
}
return cnt;
}
/**
* 傷病名を削除する。
* @param removeList 削除する傷病名のidリスト
* @return 削除数
*/
@Override
public int removeDiagnosis(List<Long> removeList) {
int cnt = 0;
for (Long id : removeList) {
RegisteredDiagnosisModel bean = em.find(RegisteredDiagnosisModel.class, id);
em.remove(bean);
cnt++;
}
return cnt;
}
/**
* Observationを取得する。
* @param spec 検索仕様
* @return Observationのリスト
*/
@SuppressWarnings("unchecked")
@Override
public List<ObservationModel> getObservations(ObservationSearchSpec spec) {
List ret = null;
String observation = spec.getObservation();
String phenomenon = spec.getPhenomenon();
Date firstConfirmed = spec.getFirstConfirmed();
if (observation != null) {
if (firstConfirmed != null) {
ret = em.createQuery("from ObservationModel o where o.karte.id=:karteId and o.observation=:observation and o.started >= :firstConfirmed")
.setParameter("karteId", spec.getKarteId())
.setParameter("observation", observation)
.setParameter("firstConfirmed", firstConfirmed)
.getResultList();
} else {
ret = em.createQuery("from ObservationModel o where o.karte.id=:karteId and o.observation=:observation")
.setParameter("karteId", spec.getKarteId())
.setParameter("observation", observation)
.getResultList();
}
} else if (phenomenon != null) {
if (firstConfirmed != null) {
ret = em.createQuery("from ObservationModel o where o.karte.id=:karteId and o.phenomenon=:phenomenon and o.started >= :firstConfirmed")
.setParameter("karteId", spec.getKarteId())
.setParameter("phenomenon", phenomenon)
.setParameter("firstConfirmed", firstConfirmed)
.getResultList();
} else {
ret = em.createQuery("from ObservationModel o where o.karte.id=:karteId and o.phenomenon=:phenomenon")
.setParameter("karteId", spec.getKarteId())
.setParameter("phenomenon", phenomenon)
.getResultList();
}
}
return ret;
}
/**
* Observationを追加する。
* @param observations 追加するObservationのリスト
* @return 追加したObservationのIdリスト
*/
@Override
public List<Long> addObservations(List<ObservationModel> observations) {
if (observations != null && observations.size() > 0) {
List<Long> ret = new ArrayList<Long>(observations.size());
for (ObservationModel model : observations) {
em.persist(model);
ret.add(Long.valueOf(model.getId()));
}
return ret;
}
return null;
}
/**
* Observationを更新する。
* @param observations 更新するObservationのリスト
* @return 更新した数
*/
@Override
public int updateObservations(List<ObservationModel> observations) {
if (observations != null && observations.size() > 0) {
int cnt = 0;
for (ObservationModel model : observations) {
em.merge(model);
cnt++;
}
return cnt;
}
return 0;
}
/**
* Observationを削除する。
* @param observations 削除するObservationのリスト
* @return 削除した数
*/
@Override
public int removeObservations(List<Long> observations) {
if (observations != null && observations.size() > 0) {
int cnt = 0;
for (Long id : observations) {
ObservationModel model = em.find(ObservationModel.class, id);
em.remove(model);
cnt++;
}
return cnt;
}
return 0;
}
/**
* 患者メモを更新する。
* @param memo 更新するメモ
*/
@Override
public int updatePatientMemo(PatientMemoModel memo) {
int cnt = 0;
if (memo.getId() == 0L) {
em.persist(memo);
} else {
em.merge(memo);
}
cnt++;
return cnt;
}
/**
* 予約を保存、更新、削除する。
* @param spec 予約情報の DTO
*/
@Override
public int putAppointments(AppointSpec spec) {
int cnt = 0;
Collection added = spec.getAdded();
Collection updated = spec.getUpdared();
Collection removed = spec.getRemoved();
AppointmentModel bean;
// 登録する
if (added != null && added.size() > 0 ) {
Iterator iter = added.iterator();
while(iter.hasNext()) {
bean = (AppointmentModel)iter.next();
em.persist(bean);
cnt++;
}
}
// 更新する
if (updated != null && updated.size() > 0 ) {
Iterator iter = updated.iterator();
while(iter.hasNext()) {
bean = (AppointmentModel)iter.next();
// av は分離オブジェクトである
em.merge(bean);
cnt++;
}
}
// 削除
if (removed != null && removed.size() > 0 ) {
Iterator iter = removed.iterator();
while(iter.hasNext()) {
bean = (AppointmentModel)iter.next();
// 分離オブジェクトは remove に渡せないので対象を検索する
AppointmentModel target = em.find(AppointmentModel.class, bean.getId());
em.remove(target);
cnt++;
}
}
return cnt;
}
/**
* 予約を検索する。
* @param spec 検索仕様
* @return 予約の Collection
*/
@Override
public List<List> getAppointmentList(ModuleSearchSpec spec) {
// 抽出期間は別けられている
Date[] fromDate = spec.getFromDate();
Date[] toDate = spec.getToDate();
int len = fromDate.length;
List<List> ret = new ArrayList<List>(len);
// 抽出期間ごとに検索しコレクションに加える
for (int i = 0; i < len; i++) {
List c = em.createQuery("from AppointmentModel a where a.karte.id = :karteId and a.date between :fromDate and :toDate")
.setParameter("karteId", spec.getKarteId())
.setParameter("fromDate", fromDate[i])
.setParameter("toDate", toDate[i])
.getResultList();
ret.add(c);
}
return ret;
}
}