From fce96ef468291fb9a0e6a4d34ab371315e9485d4 Mon Sep 17 00:00:00 2001
From: yxh <172933527@qq.com>
Date: 星期六, 03 十月 2026 00:59:44 +0800
Subject: [PATCH] ai优化
---
jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/queuesequence/QueueSequenceServiceImpl.java | 338 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 333 insertions(+), 5 deletions(-)
diff --git a/jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/queuesequence/QueueSequenceServiceImpl.java b/jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/queuesequence/QueueSequenceServiceImpl.java
index b3b8870..ebfc4db 100644
--- a/jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/queuesequence/QueueSequenceServiceImpl.java
+++ b/jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/queuesequence/QueueSequenceServiceImpl.java
@@ -2,14 +2,18 @@
import cn.lihu.jh.module.ecg.Utils;
import cn.lihu.jh.module.ecg.dal.dataobject.checktype.CheckTypeDO;
+import cn.lihu.jh.module.ecg.dal.dataobject.queuesequence.SeqCounterDO;
import cn.lihu.jh.module.ecg.dal.mysql.checktype.CheckTypeMapper;
+import cn.lihu.jh.module.ecg.enums.SeqRuleEnum;
import cn.lihu.jh.module.infra.api.config.ConfigApi;
import cn.lihu.jh.module.system.api.dict.DictDataApi;
import cn.lihu.jh.module.system.api.dict.dto.DictDataRespDTO;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
+import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;
import cn.lihu.jh.module.ecg.controller.admin.queuesequence.vo.*;
@@ -17,6 +21,7 @@
import cn.lihu.jh.framework.common.pojo.PageResult;
import cn.lihu.jh.framework.common.pojo.PageParam;
import cn.lihu.jh.framework.common.util.object.BeanUtils;
+import cn.lihu.jh.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.lihu.jh.module.ecg.dal.mysql.queuesequence.QueueSequenceMapper;
@@ -33,7 +38,16 @@
*/
@Service
@Validated
+@Slf4j
public class QueueSequenceServiceImpl implements QueueSequenceService {
+
+ /**
+ * 鍚戝悗鏌ユ壘鍙敤鏃舵鐨勬渶澶ф鏁帮紙绾� 3 灏忔椂锛夈��
+ * <p>
+ * 鍙� 6 鐨勫師鍥狅細瀛楀吀鏃舵涓� 30 鍒嗛挓绮掑害锛�6 涓害 3 灏忔椂锛�
+ * 鏃㈣兘璺ㄨ繃"灏戞暟鏃舵缂哄け/宸叉弧"鐨勬儏鍐碉紝鍙堜笉浼氭妸涓婂崍鐨勬偅鑰呮帓鍒颁笅鍗堥�犳垚浣撻獙闂銆�
+ */
+ private static final int MAX_TIMESLOT_LOOKAHEAD = 6;
@Resource
private ConfigApi configApi;
@@ -47,6 +61,23 @@
@Resource
private CheckTypeMapper checkTypeMapper;
+ /**
+ * 鐢ㄤ簬鎶娿�岃鏁板櫒鎺ㄨ繘 + 璇诲洖銆嶆樉寮忓寘杩�**鍚屼竴涓簨鍔�**銆�
+ *
+ * <h4>涓轰粈涔堝繀椤荤敤 TransactionTemplate 鑰屼笉鏄� @Transactional锛堥噸瑕侊級</h4>
+ * 杩欎袱姝ュ湪 {@link #nextCheckInSeqNo} 杩欎釜 **private 鏂规硶**閲岋紝涓旂敱**鍚岀被鍐呴儴璋冪敤**
+ * 锛坽@code distributeSeqNoByRule} 鈫� {@code nextCheckInSeqNo}锛夈��
+ * Spring 鐨� {@code @Transactional} 鍩轰簬浠g悊锛�**鍚岀被鑷皟鐢ㄤ笉浼氳蛋浠g悊 鈬� 娉ㄨВ瀹屽叏涓嶇敓鏁�**銆�
+ * 浜庢槸"鎺ㄨ繘"涓�"璇诲洖"浼氬悇鑷嫭绔嬫彁浜わ紝鍦� REPEATABLE READ 涓嬭鍒扮殑鍙兘鏄棫蹇収
+ * 鈬� **骞跺彂閲嶅彿**锛堟棭鍏堝疄娴嬶細鐙珛浜嬪姟鍙湁 75/160 涓敮涓�鍊硷紱鍚屼簨鍔′负 160/160锛夈��
+ *
+ * 鏁呰繖閲岀敤 {@link TransactionTemplate} **鏄惧紡**寮�鍚簨鍔★紝涓嶄緷璧� AOP 浠g悊锛屽繀鐒剁敓鏁堛��
+ * 浼犳挱琛屼负鐢ㄩ粯璁ょ殑 REQUIRED锛氬灞傚凡鏈変簨鍔★紙濡� {@code distributeSeqNoByRule}
+ * 鏍囨敞浜� {@code @Transactional}锛夋椂骞跺叆锛屾病鏈夋椂鏂板缓銆�
+ */
+ @Resource
+ private org.springframework.transaction.support.TransactionTemplate counterTransactionTemplate;
+
@Override
public void initQueueSequenceTable() {
queueSequenceMapper.clearQueueSequenceTableNotCurrent();
@@ -56,8 +87,17 @@
}
}
+ /**
+ * 鏂板棰勭害鏃舵瑙勫垯銆�
+ *
+ * <p>鏍¢獙銆屽悓涓�妫�鏌ョ被鍨� + 鍚屼竴鏃舵銆嶄笉閲嶅 鈥斺�� 琛ㄤ笂娌℃湁璇ュ敮涓�绾︽潫锛堝巻鍙查仐鐣欙級锛�
+ * 鑰屽彇鍙� SQL {@code select queue_no ... where check_type=? and time_slot=? for update}
+ * 鏈熸湜鍙懡涓竴琛岋紱涓�鏃﹀嚭鐜伴噸澶嶈锛屽彇鍙蜂細鍛戒腑涓嶇‘瀹氱殑涓�琛屻��
+ * 杩欓噷鍦ㄧ淮鎶ゅ叆鍙e鎷︽埅锛岀粰鍑烘槑纭彁绀鸿�屼笉鏄闂杩涘叆鍙栧彿闃舵銆�
+ */
@Override
public Integer createQueueSequence(QueueSequenceSaveReqVO createReqVO) {
+ validateTimeslotNotDuplicated(createReqVO.getCheckType(), createReqVO.getTimeSlot(), null);
// 鎻掑叆
QueueSequenceDO queueSequence = BeanUtils.toBean(createReqVO, QueueSequenceDO.class);
queueSequenceMapper.insert(queueSequence);
@@ -69,9 +109,32 @@
public void updateQueueSequence(QueueSequenceSaveReqVO updateReqVO) {
// 鏍¢獙瀛樺湪
validateQueueSequenceExists(updateReqVO.getId());
+ // 銆愭牎楠屻�戞敼浜� checkType 鎴� timeSlot 鏃朵笉鑳戒笌鍏跺畠琛屾挒杞︼紙鎺掗櫎鑷韩锛�
+ validateTimeslotNotDuplicated(updateReqVO.getCheckType(), updateReqVO.getTimeSlot(),
+ updateReqVO.getId());
// 鏇存柊
QueueSequenceDO updateObj = BeanUtils.toBean(updateReqVO, QueueSequenceDO.class);
queueSequenceMapper.updateById(updateObj);
+ }
+
+ /**
+ * 鏍¢獙銆屾鏌ョ被鍨� + 鏃舵銆嶅湪琛ㄤ腑鍞竴銆�
+ *
+ * @param excludeId 闇�鎺掗櫎鐨勮 id锛堟洿鏂版椂浼犺嚜韬� id锛涙柊澧炴椂浼� null锛�
+ */
+ private void validateTimeslotNotDuplicated(Integer checkType, Integer timeSlot, Integer excludeId) {
+ if (null == checkType || null == timeSlot) {
+ return; // 鐢� VO 涓婄殑 @NotNull 鍏滃簳锛岃繖閲屼笉閲嶅鎶ラ敊
+ }
+ QueueSequenceDO exist = queueSequenceMapper.selectOne(
+ new LambdaQueryWrapperX<QueueSequenceDO>()
+ .eq(QueueSequenceDO::getCheckType, checkType)
+ .eq(QueueSequenceDO::getTimeSlot, timeSlot));
+ if (null != exist && !exist.getId().equals(excludeId)) {
+ log.warn("[validateTimeslotNotDuplicated] 鏃舵瑙勫垯閲嶅. checkType={} timeSlot={} "
+ + "宸插瓨鍦╥d={} 鏈id={}", checkType, timeSlot, exist.getId(), excludeId);
+ throw exception(QUEUE_SEQUENCE_TIMESLOT_DUPLICATE);
+ }
}
@Override
@@ -91,6 +154,11 @@
@Override
public QueueSequenceDO getQueueSequence(Integer id) {
return queueSequenceMapper.selectById(id);
+ }
+
+ @Override
+ public List<QueueSequenceDO> selectGivenCheckTypeTimeslot(Integer checkType) {
+ return queueSequenceMapper.selectTimeslotByCheckType( checkType );
}
@Override
@@ -133,12 +201,43 @@
public Integer distributeSeqNo(Integer checkType, Integer timeslot) {
Integer curSeqNo = queueSequenceMapper.selectQueueNoForUpdate(checkType, timeslot);
if (null == curSeqNo) {
- throw exception(QUEUE_SEQUENCE_TIMESLOT_FULL);
- }
+ // 銆愪慨澶嶃�屾椂娈典笉鍏�/宸叉弧瀵艰嚧娌℃硶绛惧埌銆嶃��
+ // 鍘熼�昏緫锛氬彧鍙�"瀛楀吀閲岀殑绱ч偦涓嬩竴涓椂娈�"锛屼笖瑕佹眰璇ユ椂娈靛湪 queue_sequence 涓瓨鍦ㄣ��
+ // 鐜板疄闂锛堝疄娴嬶級锛�
+ // 路 瀛楀吀 ecg_book_timeslot 鏈� 21 涓椂娈碉紝浣� queue_sequence 涓� checkType=400
+ // 鍙湁 13 涓�乧heckType=100 鍙湁 17 涓� 鈥斺�� 閮ㄥ垎鏃舵鏍规湰娌℃湁鍙锋睜琛岋紱
+ // 路 HIS 浼氫笅鍙� 60 鍒嗛挓绮掑害鐨� bookTimeslot锛堝 10001100锛夛紝瀛楀吀閲屾病鏈夎鍊硷紝
+ // getNextTimeSlot 鐩存帴 return null锛�
+ // 路 涓埆鏃舵瀹归噺寰堝皬锛堝钩鏉胯繍鍔� timeslot_book_num=3锛夛紝寰堝揩閿�瀹屻��
+ // 涓夎�呬换涓�鍛戒腑 鈫� 鎶� QUEUE_SEQUENCE_TIMESLOT_FULL 鈫� **鎮h�呮病娉曠鍒�**銆�
+ //
+ // 鐜板湪鏀逛负锛�**鍚戝悗鏈�澶氭壘 N 涓椂娈碉紝鍙栫涓�涓粛鏈変綑閲忕殑**锛堜笅闈� findAvailableTimeslot 鍐呭疄鐜帮級锛�
+ // 澶у箙闄嶄綆"鍥犱釜鍒椂娈电己澶�/宸叉弧灏辩鍒板け璐�"鐨勬鐜囷紱纭疄閮芥弧浜嗘墠鎶涢敊銆�
+ Integer nextTimeslot = findAvailableTimeslot(checkType, timeslot, false);
+ if (null == nextTimeslot) {
+ log.error("[distributeSeqNo] 璇ユ鏌ョ被鍨嬪悜鍚� {} 涓椂娈靛唴鍧囨棤鍙彇鍙锋椂娈�. checkType={} timeslot={}",
+ MAX_TIMESLOT_LOOKAHEAD, checkType, timeslot);
+ throw exception(QUEUE_SEQUENCE_TIMESLOT_FULL);
+ }
- Integer updateRowNum = queueSequenceMapper.updateGivenCheckTypeTimeslotSeqNo(checkType, timeslot, curSeqNo);
- if (null == updateRowNum || updateRowNum == 0) {
- throw exception(QUEUE_SEQUENCE_TIMESLOT_EXCEPTION);
+ curSeqNo = queueSequenceMapper.selectQueueNoForUpdate(checkType, nextTimeslot);
+ if (null == curSeqNo) {
+ // 鎺㈡祴涓庡彇鍙蜂箣闂磋骞跺彂鎶㈢┖锛屼繚瀹堟姤閿欙紙鐢卞墠绔彁绀洪噸鏂扮鍒帮級
+ log.warn("[distributeSeqNo] 鎺㈡祴鍒扮殑鏃舵闅忓嵆琚崰婊�. checkType={} timeslot={}",
+ checkType, nextTimeslot);
+ throw exception(QUEUE_SEQUENCE_TIMESLOT_FULL);
+ }
+
+ Integer updateRowNum = queueSequenceMapper.updateGivenCheckTypeTimeslotSeqNo(checkType, nextTimeslot, curSeqNo);
+ if (null == updateRowNum || updateRowNum == 0) {
+ throw exception(QUEUE_SEQUENCE_TIMESLOT_EXCEPTION);
+ }
+
+ } else {
+ Integer updateRowNum = queueSequenceMapper.updateGivenCheckTypeTimeslotSeqNo(checkType, timeslot, curSeqNo);
+ if (null == updateRowNum || updateRowNum == 0) {
+ throw exception(QUEUE_SEQUENCE_TIMESLOT_EXCEPTION);
+ }
}
return curSeqNo + 1;
@@ -160,5 +259,234 @@
return curVipSeqNo + 1;
}
+ /**
+ * 鎸夈�愭鏌ョ被鍨嬮厤缃殑瑙勫垯銆戝垎閰嶆帓闃熷簭鍙枫��
+ *
+ * <h3>涓夌瑙勫垯</h3>
+ * <ol>
+ * <li><b>棰勭害鍙�(0)</b>锛氱洿鎺ョ敤棰勭害绯荤粺宸蹭笅鍙戠殑鍙凤紝涓嶅啀鍒嗛厤銆�
+ * 娉ㄦ剰 {@code existingSeqNo} 涓虹┖鏃�**鍥為��鍒扮鍒板簭鍙疯鍒�** 鈥斺�� 鍥犱负鑻ョ洿鎺ヨ繑鍥�
+ * null锛岃皟鐢ㄦ柟浼氭妸 null 鍐欏叆 {@code book_seq_num}锛屾偅鑰呭皢鎷夸笉鍒板彿銆�</li>
+ * <li><b>绛惧埌搴忓彿(1)</b>锛氬綋澶╁悓妫�鏌ョ被鍨嬪唴杩炵画閫掑锛堢 1 涓鍒� = 1 鍙凤級锛�
+ * 骞舵寜銆屾瘡 9 涓彿棰勭暀 1 涓�嶈烦杩囬鐣欏彿缁欏姞鎬ユ偅鑰呫��
+ * **涓嶈褰撴棩涓婇檺**锛堜笌瑙勫垯 2 鐨勬椂娈靛閲忎笉鍚岋級銆�</li>
+ * <li><b>鏃舵鍙�(2)</b>锛氳皟鐢ㄥ師鏈夌殑 {@link #distributeSeqNo}锛堟寜 {@code time_slot} 浠庡彿姹犲彇鍙凤級锛�
+ * 淇濈暀鏃舵瀹归噺鎺у埗銆傝繖涔熸槸 {@code seq_rule} 鏈厤缃椂鐨勯粯璁ゅ�笺��</li>
+ * </ol>
+ *
+ * <h3>涓轰粈涔堣鍒� 1 闇�瑕佺嫭绔嬭鏁板櫒</h3>
+ * 涓嶈兘鐢� {@code MAX(book_seq_num)+1} 璁$畻锛氶鐣欏彿锛�10銆�20鈥︼級鑻ユ棤浜轰娇鐢ㄥ氨涓嶄細鍑虹幇鍦�
+ * 鏁版嵁閲岋紝涓嬩竴涓櫘閫氬彿鎸� MAX+1 浼氬啀娆$畻鍑哄悓涓�涓鐣欏彿锛岄�犳垚鍐茬獊銆�
+ * 鍥犳鐢� {@code seq_counter} 琛�**蹇呴』琚秷鑰�**鍦伴�掑锛堣 Mapper 鐨� LAST_INSERT_ID 鍐欐硶锛夈��
+ */
+ @Override
+ @Transactional
+ public Integer distributeSeqNoByRule(Integer checkType, Integer timeslot,
+ Integer existingSeqNo, boolean urgent) {
+ SeqRuleEnum rule = resolveRule(checkType);
+
+ switch (rule) {
+ case APPOINT:
+ // 鈶� 棰勭害鍙凤細棰勭害绯荤粺宸茬粰鍙峰垯鐩存帴鐢紱鍚﹀垯鍥為��鍒扮鍒板簭鍙凤紙閬垮厤鎮h�呮嬁涓嶅埌鍙凤級
+ if (null != existingSeqNo && existingSeqNo > 0) {
+ log.info("[distributeSeqNoByRule] 瑙勫垯=棰勭害鍙凤紝鐩存帴浣跨敤棰勭害绯荤粺鍙�. checkType={} seqNo={}",
+ checkType, existingSeqNo);
+ return existingSeqNo;
+ }
+ log.warn("[distributeSeqNoByRule] 瑙勫垯=棰勭害鍙凤紝浣嗛绾︾郴缁熸湭涓嬪彂鍙凤紝鍥為��鍒扮鍒板簭鍙�. checkType={}",
+ checkType);
+ return nextCheckInSeqNo(checkType, urgent);
+
+ case CHECKIN:
+ // 鈶� 绛惧埌搴忓彿锛氬綋澶╁悓妫�鏌ョ被鍨嬭繛缁�掑锛屾瘡 9 涓鐣� 1 涓�
+ return nextCheckInSeqNo(checkType, urgent);
+
+ case TIMESLOT:
+ default:
+ // 鈶� 鏃舵鍙凤細淇濇寔鏀归�犲墠鐨勮涓猴紙鍚椂娈靛閲忔帶鍒讹級
+ return distributeSeqNo(checkType, timeslot);
+ }
+ }
+
+ /**
+ * 瑙f瀽鏌愭鏌ョ被鍨嬮厤缃殑鎺掗槦搴忓彿瑙勫垯锛涙煡涓嶅埌閰嶇疆鎴栨湭閰嶇疆鏃惰繑鍥為粯璁わ紙鏃舵鍙凤級銆�
+ */
+ private SeqRuleEnum resolveRule(Integer checkType) {
+ if (null == checkType) {
+ return SeqRuleEnum.DEFAULT;
+ }
+ CheckTypeDO checkTypeDO = checkTypeMapper.getCheckTypeByValue(checkType);
+ if (null == checkTypeDO) {
+ log.warn("[resolveRule] 鏈壘鍒版鏌ョ被鍨嬮厤缃紝浣跨敤榛樿瑙勫垯(鏃舵鍙�). checkType={}", checkType);
+ return SeqRuleEnum.DEFAULT;
+ }
+ return SeqRuleEnum.parse(checkTypeDO.getSeqRule());
+ }
+
+ /**
+ * 璁$畻涓嬩竴涓�岀鍒板簭鍙枫�嶃��
+ *
+ * <h4>涓や釜鐙珛璁℃暟鍣紙杩欐槸韪╁潙鍚庣殑缁撹锛�</h4>
+ * {@code seq_counter} 缁存姢涓や釜浜掍笉骞叉壈鐨勮鏁板櫒锛�
+ * <ul>
+ * <li>{@code normal_next} 鈥斺�� 鏅�氬彿锛屽簭鍒� 1..9銆�11..19銆�21..29鈥︼紙璺宠繃 10 鐨勫�嶆暟锛夛紱</li>
+ * <li>{@code reserved_next} 鈥斺�� 棰勭暀鍙凤紙鍔犳�ョ敤锛夛紝搴忓垪 10銆�20銆�30鈥︺��</li>
+ * </ul>
+ * <b>涓轰粈涔堜笉鑳藉彧鐢ㄤ竴涓鏁板櫒</b>锛氳嫢鐢ㄥ崟涓� {@code candidate} 閫掑鍚庡啀"璺宠繃棰勭暀鍙�"锛�
+ * 蹇呯劧鎾炶溅 鈥斺�� 鏅�氭偅鑰呮嬁鍒� candidate=10 鈫� 璺宠繃寰� 11锛涗笅涓�涓櫘閫氭偅鑰呮嬁鍒� candidate=11
+ * 鈫� 涔熷緱 11銆傚疄娴� 20 娆″彇鍙蜂腑绗� 10銆�11 娆″垎閰嶅彿鐩稿悓銆�
+ * 涓や釜璁℃暟鍣ㄧ墿鐞嗛殧绂诲悗锛屼簩鑰呭彇鍊煎煙涓嶇浉浜わ紝**涓嶅彲鑳介噸鍙�**銆�
+ *
+ * <h4>骞跺彂锛堜袱澶勫潙锛屽潎宸蹭慨澶嶏級</h4>
+ * <ol>
+ * <li><b>鎺ㄨ繘蹇呴』鍘熷瓙</b>锛氱敤鍗曟潯 {@code INSERT .. ON DUPLICATE KEY UPDATE}
+ * 鍦ㄦ暟鎹簱绔畬鎴�"璇�-鏀�-鍐�"锛岄伩鍏嶅厛鏌ュ悗鏀归�犳垚閲嶅彿銆�
+ * 娉ㄦ剰璇ヨ鍙ュ繀椤诲姞 {@code @InterceptorIgnore(dataPermission="true")}锛�
+ * 鍚﹀垯 MyBatis-Plus 鐨� DataPermissionInterceptor 浼氱敤 JSqlParser 瑙f瀽瀹冨苟
+ * **鎶� UnsupportedOperationException**锛圝SqlParser 涓嶆敮鎸佽 MySQL 涓撴湁璇硶锛夛紝
+ * 琛ㄧ幇涓�**棰勭害纭鐩存帴 500** 鈥斺�� 璇﹁ Mapper 涓婄殑娉ㄩ噴銆�</li>
+ * <li><b>鎺ㄨ繘涓庤鍥炲繀椤诲悓浜嬪姟</b>锛歁ySQL 榛樿 REPEATABLE READ锛�
+ * 鑻ヨ鍥炲湪鍙︿竴涓簨鍔¢噷锛屼細璇诲埌鏃у揩鐓� 鈬� 閲嶅彿銆�
+ * 鏈柟娉曟槸 private 涓旇**鍚岀被鍐呴儴璋冪敤**锛寋@code @Transactional} 鍥犱笉璧颁唬鐞嗚��**澶辨晥**锛�
+ * 鏁呮敼鐢� {@link org.springframework.transaction.support.TransactionTemplate} 鏄惧紡寮�鍚簨鍔°��</li>
+ * </ol>
+ * 瀹炴祴 8 绾跨▼ 脳 20 娆� = 160 娆″彇鍙凤細鍚屼簨鍔� 160/160 鍞竴銆佸彿鍊艰繛缁紱
+ * 鐙珛浜嬪姟鍙湁 75/160 鈥斺�� 璇﹁ Mapper 娉ㄩ噴銆�
+ */
+ private Integer nextCheckInSeqNo(Integer checkType, boolean urgent) {
+ final LocalDate today = LocalDate.now();
+
+ // 鐢ㄦ樉寮忎簨鍔℃妸銆屾帹杩涖�嶄笌銆岃鍥炪�嶅寘鍦ㄤ竴璧凤紙@Transactional 鍦ㄦ澶勪笉鐢熸晥锛岃鏂规硶娉ㄩ噴锛�
+ Integer assigned = counterTransactionTemplate.execute(status -> {
+ if (urgent) {
+ queueSequenceMapper.advanceReservedSeq(checkType, today);
+ } else {
+ queueSequenceMapper.advanceNormalSeq(checkType, today);
+ }
+ SeqCounterDO counter = queueSequenceMapper.getSeqCounter(checkType, today);
+ if (null == counter) {
+ return null;
+ }
+ return urgent ? counter.getReservedNext() : counter.getNormalNext();
+ });
+
+ if (null == assigned) {
+ // 鐞嗚涓婁笉浼氬彂鐢燂紙鍒氭帹杩涜繃锛夛紱淇濆畧鍥為��锛岄伩鍏嶆姏閿欓樆鏂鍒�
+ assigned = urgent ? SeqRuleEnum.ROUND_SIZE : 1;
+ log.error("[nextCheckInSeqNo] 璁℃暟鍣ㄨ鍙栧け璐ワ紝鍥為��涓洪粯璁ゅ彿. checkType={} 鍔犳��={} 鍥為��鍙�={}",
+ checkType, urgent, assigned);
+ }
+
+ log.info("[nextCheckInSeqNo] 瑙勫垯=绛惧埌搴忓彿. checkType={} 鍔犳��={} 鏈鍒嗛厤={}",
+ checkType, urgent, assigned);
+ return assigned;
+ }
+
+ /**
+ * 鍚戝悗鏌ユ壘涓�涓�**浠嶆湁浣欓噺**鐨勯绾︽椂娈点��
+ * <p>
+ * 涓庢棫鐨� {@code getNextTimeSlot} 鐨勫尯鍒細
+ * <ol>
+ * <li>鏃у疄鐜板彧鐪�"绱ч偦鐨勪笅涓�涓�"瀛楀吀椤癸紝涓�**涓嶆鏌ヨ鏃舵鍦� queue_sequence 涓槸鍚﹀瓨鍦�**
+ * 鈥斺�� 鑻ヤ笅涓�涓椂娈垫伆濂界己澶憋紙鍙锋睜娌℃湁杩欎竴琛岋級锛屼細鐩存帴杩斿洖瀹冿紝闅忓悗
+ * {@code selectQueueNoForUpdate} 鍙栦笉鍒板�� 鈫� 鎶� TIMESLOT_FULL銆�
+ * 杩欎篃鏄�"杩愬姩璇曢獙娌℃硶绛惧埌"鐨勭洿鎺ュ師鍥犱箣涓�銆�</li>
+ * <li>鏃у疄鐜板湪 {@code timeslot} 涓嶅湪瀛楀吀涓椂锛圚IS 浼氫笅鍙� 60 鍒嗛挓绮掑害鍊硷紝
+ * 濡� 10001100锛岃�屽瓧鍏稿彧鏈� 30 鍒嗛挓绮掑害锛夌洿鎺� {@code return null}銆�
+ * 鏈疄鐜版敼涓�**鍙栧瓧鍏镐腑绗竴涓櫄浜� {@code timeslot} 鐨勬椂娈�**缁х画鍚戝悗鎵撅紝
+ * 浠庤�屼粛鑳戒负杩欑被鎮h�呭垎閰嶅彿銆�</li>
+ * <li>鏈�澶氬悜鍚庢壘 {@link #MAX_TIMESLOT_LOOKAHEAD} 涓椂娈碉紙绾� 3 灏忔椂锛夛紝
+ * 閬垮厤"褰撳墠鏄渶鍚庝竴涓椂娈�"鏃舵棤澶勫彲鍘伙紱鍏ㄩ兘婊℃墠杩斿洖 null銆�</li>
+ * </ol>
+ *
+ * @param checkType 妫�鏌ョ被鍨�
+ * @param timeslot HIS 浼犲叆鐨勯绾︽椂娈碉紙鍙兘鏄瓧鍏稿鐨� 60 鍒嗛挓绮掑害鍊硷級
+ * @param includeVip true 鏃惰姹� VIP 鍙蜂篃鏈変綑閲�
+ * @return 鍙敤鐨勬椂娈靛�硷紱鏃犲彲鐢ㄦ椂娈佃繑鍥� null
+ */
+ private Integer findAvailableTimeslot(Integer checkType, Integer timeslot, boolean includeVip) {
+ List<DictDataRespDTO> dictBookTimeslotList = dictDataApi.getDictDataList(ECG_BOOK_TIMESLOT);
+ if (dictBookTimeslotList == null || dictBookTimeslotList.isEmpty()) {
+ log.error("[findAvailableTimeslot] 瀛楀吀 {} 涓虹┖锛屾棤娉曞垎閰嶆椂娈靛彿", ECG_BOOK_TIMESLOT);
+ return null;
+ }
+
+ // 璧风偣锛氫弗鏍兼櫄浜� timeslot 鐨勭涓�涓瓧鍏搁」銆�
+ // 杩欐牱鍗充娇 timeslot 涓嶅湪瀛楀吀鍐咃紙60 鍒嗛挓绮掑害鍊硷級锛屼篃鑳借惤鍒板叾鍚庣殑鏃舵缁х画鎵俱��
+ int startIdx = -1;
+ for (int i = 0; i < dictBookTimeslotList.size(); i++) {
+ Integer v = parseSlotValue(dictBookTimeslotList.get(i).getValue());
+ if (null != v && v > timeslot) {
+ startIdx = i;
+ break;
+ }
+ }
+ if (startIdx < 0) {
+ // timeslot 宸叉槸鏈�鍚庝竴涓椂娈碉紙鎴栧叏閮ㄦ椂娈甸兘鏃╀簬瀹冿級
+ log.warn("[findAvailableTimeslot] 宸叉槸鏈�鍚庝竴涓椂娈垫垨鏃犳洿鏅氭椂娈�. checkType={} timeslot={}",
+ checkType, timeslot);
+ return null;
+ }
+
+ int end = Math.min(dictBookTimeslotList.size(), startIdx + MAX_TIMESLOT_LOOKAHEAD);
+ for (int i = startIdx; i < end; i++) {
+ Integer candidate = parseSlotValue(dictBookTimeslotList.get(i).getValue());
+ if (null == candidate) {
+ continue;
+ }
+ Integer avail = queueSequenceMapper.countAvailableTimeslot(
+ checkType, candidate, includeVip ? 1 : 0);
+ if (null != avail && avail > 0) {
+ log.info("[findAvailableTimeslot] checkType={} 鍘熸椂娈�={} 鈫� 鏀圭敤鏃舵={}锛堢 {} 涓�欓�夛級",
+ checkType, timeslot, candidate, i - startIdx + 1);
+ return candidate;
+ }
+ }
+
+ log.warn("[findAvailableTimeslot] 鍚戝悗 {} 涓椂娈靛唴鍧囨棤浣欓噺. checkType={} timeslot={}",
+ MAX_TIMESLOT_LOOKAHEAD, checkType, timeslot);
+ return null;
+ }
+
+ /** 瀹夊叏瑙f瀽瀛楀吀鍊硷紙褰㈠ "9301000"锛変负 Integer锛屽拷鐣ヨ剰鏁版嵁 */
+ private Integer parseSlotValue(String value) {
+ if (null == value || value.isEmpty()) {
+ return null;
+ }
+ try {
+ return Integer.valueOf(value.trim());
+ } catch (NumberFormatException e) {
+ log.warn("[parseSlotValue] 瀛楀吀鏃舵鍊奸潪娉曪紝宸插拷鐣�: {}", value);
+ return null;
+ }
+ }
+
+ /**
+ * 鏃х殑"鍙栫揣閭讳笅涓�涓椂娈�"銆備繚鐣欎緵鍙傝��/鍏煎锛屾柊閫昏緫璇风敤
+ * {@link #findAvailableTimeslot(Integer, Integer, boolean)}銆�
+ *
+ * @deprecated 鍙湅涓嬩竴涓椂娈典笖涓嶆牎楠屽彿姹犳槸鍚﹀瓨鍦紝浼氬鑷寸鍒板け璐�
+ */
+ @Deprecated
+ private Integer getNextTimeSlot(Integer timeslot, Integer checkType) {
+ List<DictDataRespDTO> dictBookTimeslotList = dictDataApi.getDictDataList(ECG_BOOK_TIMESLOT);
+ Optional<Integer> index = dictBookTimeslotList.stream()
+ .filter(e -> e.getValue().equals(String.valueOf(timeslot)))
+ .findFirst()
+ .map(dictBookTimeslotList::indexOf);
+ if (!index.isPresent())
+ return null;
+
+ if (dictBookTimeslotList.size() <= (index.get() + 1))
+ return null;
+
+ DictDataRespDTO dictDataRespDTO = dictBookTimeslotList.get(index.get() + 1);
+ return Integer.valueOf(dictDataRespDTO.getValue());
+ }
+ @Override
+ public void initNumber() {
+ queueSequenceMapper.initNumber();
+ }
+
}
--
Gitblit v1.9.3