完善大理快捷支付接口
diff --git a/backend/src/main/java/com/supwisdom/dlpay/medical/domain/TBMedicalDtl.java b/backend/src/main/java/com/supwisdom/dlpay/medical/domain/TBMedicalDtl.java
index bcff4b8..a3d48f9 100644
--- a/backend/src/main/java/com/supwisdom/dlpay/medical/domain/TBMedicalDtl.java
+++ b/backend/src/main/java/com/supwisdom/dlpay/medical/domain/TBMedicalDtl.java
@@ -28,7 +28,7 @@
   @Column(name = "payamount", precision = 10, scale = 3)
   private Double payamount;
   /**
-   * 本地扣费实际金额
+   * 本地扣费实际金额(即向paypai扣费的金额)
    */
   @Column(name = "mergingsubtotal", precision = 10, scale = 3)
   private Double mergingsubtotal;
diff --git a/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java b/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
index 5506ef4..bf74bb4 100644
--- a/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
+++ b/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
@@ -30,11 +30,11 @@
   public static final String SYSPARA_MEDICAL_SAVEJSON = "medical.savejson";
   public static final String SYSPARA_MEDICAL_REFUND_TOKEN = "medical.refund.token";
 
-  public static final String ARTICLE_STATUS_SAVE = "save";
-  public static final String ARTICLE_STATUS_PASS = "pass";
-  public static final String ARTICLE_STATUS_RELEASED = "released";
-  public static final String ARTICLE_STATUS_REVIEW = "review";
-  public static final String ARTICLE_STATUS_REJECT = "reject";
+  public static final String ARTICLE_STATUS_SAVE = "save";//已保存
+  public static final String ARTICLE_STATUS_PASS = "pass";//审核通过
+  public static final String ARTICLE_STATUS_RELEASED = "released";//已发布
+  public static final String ARTICLE_STATUS_REVIEW = "review";//审核中
+  public static final String ARTICLE_STATUS_REJECT = "reject";//过审失败
 
   public static final String POINTTASK_STATUS_UNDONE = "undone";//未完成
   public static final String POINTTASK_STATUS_DONE = "done";//已完成未领取
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/MedicalApi.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/MedicalApi.kt
index da3986c..45dbe3c 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/MedicalApi.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/MedicalApi.kt
@@ -4,16 +4,12 @@
 import com.supwisdom.dlpay.framework.service.SystemUtilService
 import com.supwisdom.dlpay.framework.util.DataUtil
 import com.supwisdom.dlpay.framework.util.TradeDict
-import com.supwisdom.dlpay.medical.bean.AddPatientBean
-import com.supwisdom.dlpay.medical.bean.ConfirmRequestBean
-import com.supwisdom.dlpay.medical.bean.PaymentRequestBean
-import com.supwisdom.dlpay.medical.bean.YnrccPayRequestBean
+import com.supwisdom.dlpay.medical.bean.*
 import com.supwisdom.dlpay.medical.exception.MedicineException
 import com.supwisdom.dlpay.medical.service.MedicalService
 import com.supwisdom.dlpay.medical.task.NotifyHISAsyncTask
 import com.supwisdom.dlpay.medical.util.MedicalConstant
 import com.supwisdom.dlpay.mobile.domain.TBMobileUser
-import com.supwisdom.dlpay.mobile.exception.UserLoginFailException
 import com.supwisdom.dlpay.mobile.service.MobileApiService
 import com.supwisdom.dlpay.paysdk.proxy.UserProxy
 import com.supwisdom.dlpay.util.ConstantUtil
@@ -635,10 +631,10 @@
     }
 
     /**
-     * 快捷支付支付功能
+     * 快捷支付初始化
      */
     @PostMapping("/quickpay/payinit")
-    fun quickPayInit(@RequestBody bean: YnrccPayRequestBean): JsonResult? {
+    fun quickPayInit(@RequestBody beanInit: YnrccPayInitRequestBean): JsonResult? {
         try {
             val p = SecurityContextHolder.getContext().authentication
             val user = mobileApiService.findUserById(p.name)
@@ -656,7 +652,7 @@
                 mobileApiService.saveUser(user)
             }
             val encoder = BCryptPasswordEncoder()
-            if (!encoder.matches(bean.paypwd, user.paypwd)) {
+            if (!encoder.matches(beanInit.paypwd, user.paypwd)) {
                 user.updatePaypwderror(false).also {
                     if (it) mobileApiService.saveUser(user)
                 }
@@ -669,14 +665,61 @@
             if (TradeDict.STATUS_NORMAL != user.status) {
                 return JsonResult.error("用户状态异常")
             }
-            medicalService.medicalYnrccPayInit(bean)
-            return JsonResult.ok()
+            val cardResponse = userProxy.queryCard(QueryCardParam().apply {
+                this.userid = beanInit.userid
+                this.cardtype = ConstantUtil.CARDTYPE_BANKCARD
+            })
+            if (cardResponse.retcode != MedicalConstant.PAYAPI_SUCCESS_RETCODE) {
+                logger.error { "查询用户[${beanInit.userid}]银行卡失败:${cardResponse.retmsg}" }
+                return JsonResult.error("支付失败:查询用户银行卡失败")
+            }
+            if (!cardResponse.card.signed) {
+                return JsonResult.error("请签约银行卡")
+            }
+            medicalService.medicalPayPreInit(user.uid, PaymentRequestBean().apply {
+                this.billno = beanInit.billno
+                this.cardNo = cardResponse.card.cardno
+            })
+            beanInit.uid = user.uid
+            beanInit.userid = user.userid
+            beanInit.bankcardno = cardResponse.card.cardno
+            val data = medicalService.medicalYnrccPayInit(beanInit)
+            return JsonResult.ok().put("data", data)
         } catch (e: Exception) {
             logger.error("系统异常", e)
             if (e is MedicineException) {
                 return JsonResult.error(e.message)
             }
-            return JsonResult.error("系统异常,解约失败")
+            return JsonResult.error("系统异常,支付失败")
+        }
+    }
+
+    /**
+     * 快捷支付确认
+     */
+    @PostMapping("/quickpay/confirm")
+    fun quickPayConfirm(@RequestBody bean: YnrccPayConfirmRequestBean): JsonResult? {
+        try {
+            val p = SecurityContextHolder.getContext().authentication
+            val user = mobileApiService.findUserById(p.name)
+                    ?: return JsonResult.error("用户不存在,请注册")
+            bean.uid = user.uid
+            val medicalDtl = medicalService.medicalYnrccPayConfirm(bean)
+            val jsonResult = JsonResult.ok()
+            if (medicalDtl.paystatus == MedicalConstant.DTL_STATUS_SUCCESS) {
+                notifyHISAsyncTask.notifyHIS(bean.billno)
+            } else if (medicalDtl.paystatus == MedicalConstant.DTL_STATUS_FAIL) {
+                jsonResult.setMessage(medicalDtl.remark)
+            }
+            return jsonResult.put("status", medicalDtl.paystatus)
+                    ?.put("transdate", medicalDtl.transdate)
+                    ?.put("transtime", medicalDtl.transtime)
+        } catch (e: Exception) {
+            logger.error("系统异常", e)
+            if (e is MedicineException) {
+                return JsonResult.error(e.message)
+            }
+            return JsonResult.error("系统异常,支付失败")
         }
     }
 }
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayConfirmRequestBean.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayConfirmRequestBean.kt
new file mode 100644
index 0000000..ae121ab
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayConfirmRequestBean.kt
@@ -0,0 +1,8 @@
+package com.supwisdom.dlpay.medical.bean
+
+class YnrccPayConfirmRequestBean {
+    var billno: String = ""
+    var plain: String = ""
+    var signature: String = ""
+    var uid: String? = ""
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayInitRequestBean.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayInitRequestBean.kt
new file mode 100644
index 0000000..c79c701
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayInitRequestBean.kt
@@ -0,0 +1,9 @@
+package com.supwisdom.dlpay.medical.bean
+
+class YnrccPayInitRequestBean {
+    var billno: String = ""
+    var uid: String? = ""
+    var userid: String? = ""
+    var bankcardno: String? = ""
+    var paypwd: String = ""
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayRequestBean.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayRequestBean.kt
deleted file mode 100644
index 970625d..0000000
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/bean/YnrccPayRequestBean.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.supwisdom.dlpay.medical.bean
-
-class YnrccPayRequestBean {
-    var billno: String = ""
-    var paypwd: String = ""
-    var uid: String = ""
-    var userid: String = ""
-}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/MedicalService.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/MedicalService.kt
index d0fad00..ef239cc 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/MedicalService.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/MedicalService.kt
@@ -1,5 +1,6 @@
 package com.supwisdom.dlpay.medical.service
 
+import com.supwisdom.dlpay.api.bean.YnrccPayOrderResponse
 import com.supwisdom.dlpay.framework.jpa.page.Pagination
 import com.supwisdom.dlpay.medical.bean.*
 import com.supwisdom.dlpay.medical.domain.TBHospital
@@ -81,11 +82,17 @@
     fun refundMedicalDtlConfirm(originBillNo: String, refundMedicalDtl: TBMedicalDtl): TBMedicalDtl
 
     @Transactional
+    fun refundQuickPayConfirm(originBillNo: String, refundMedicalDtl: TBMedicalDtl): TBMedicalDtl
+
+    @Transactional
     fun queryRefundResult(bean: PayRefundRequestBean): TBMedicalDtl
 
     @Transactional
     fun getHospitalByCode(code: String): TBHospital?
 
     @Transactional
-    fun medicalYnrccPayInit(bean: YnrccPayRequestBean)
+    fun medicalYnrccPayInit(bean: YnrccPayInitRequestBean): YnrccPayOrderResponse
+
+    @Transactional
+    fun medicalYnrccPayConfirm(bean: YnrccPayConfirmRequestBean): TBMedicalDtl
 }
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/impl/MedicalServiceImpl.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/impl/MedicalServiceImpl.kt
index 4c609a1..c6d2175 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/impl/MedicalServiceImpl.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/medical/service/impl/MedicalServiceImpl.kt
@@ -18,7 +18,6 @@
 import com.supwisdom.dlpay.paysdk.proxy.TransactionProxy
 import com.supwisdom.dlpay.paysdk.proxy.UserProxy
 import com.supwisdom.dlpay.portal.util.PortalConstant
-import com.supwisdom.dlpay.util.ConstantUtil
 import mu.KotlinLogging
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.data.domain.PageRequest
@@ -385,6 +384,9 @@
     override fun medicalPayPreInit(uid: String, bean: PaymentRequestBean) {
         val medicalDtl = medicalDtlDao.findByBillnoForUpdate(bean.billno)
                 ?: throw MedicineException("未找到流水为[${bean.billno}]的流水")
+        if (medicalDtl.uid != uid) {
+            throw MedicineException("未找到该流水")
+        }
         // 手动输入就诊卡号查询流水,可能存在多个用户支付同一笔医疗账单的情况,查询该账单是否已被支付
         val payStatus = ArrayList<String>()
         payStatus.addAll(arrayOf(MedicalConstant.DTL_STATUS_WIP, MedicalConstant.DTL_STATUS_SUCCESS))
@@ -396,6 +398,9 @@
         if (medicalDtl.paystatus != null && medicalDtl.paystatus != MedicalConstant.DTL_STATUS_INIT) {
             throw MedicineException("该流水状态异常,请查询后再试")
         }
+        if (medicalDtl.reverseflag != TradeDict.TRADE_FLAG_IN) {
+            throw MedicineException("该流水类型异常,无法支付")
+        }
         //1.从HIS中找到待支付流水
         val unPayedResponse = getUnPayedResponse(
                 medicalDtl.organizationid, medicalDtl.patientid, MedicalConstant.ORDER_QUERYTYPE_PATIENTID)
@@ -466,8 +471,8 @@
             throw MedicineException("该流水状态异常,请查询后再试")
         }
         // 查询是否已初始化
-        // TODO: 不同医院是否共用一个shopaccno
-        val shopaccno = systemUtilService.getBusinessValue(PortalConstant.SYSPARA_MEDICAL_SHOPACCNO)
+        val hospital = hospitalDao.findByHospitalcodeAndStatus(medicalDtl.organizationid, TradeDict.STATUS_NORMAL)
+                ?: throw MedicineException("未找到收款医院")
         val queryResult = transactionProxy.queryDtlResult(QueryDtlResultParam().apply {
             this.billno = medicalDtl.billno
             this.shopaccno = shopaccno
@@ -475,6 +480,7 @@
         if (MedicalConstant.PAYAPI_SUCCESS_RETCODE == queryResult.retcode) {
             //  查询到该流水说明已初始化,可直接确认
             medicalDtl.refno = queryResult.refno
+            medicalDtl.shopaccno = hospital.shopaccno
         } else if (MedicalConstant.PAYAPI_NOT_EXISTS == queryResult.retcode) {
             //  未查到该流水,继续初始化步骤
             val initParam = CitizenCardPayinitParam().apply {
@@ -492,7 +498,7 @@
                 throw MedicineException("系统异常,支付失败")
             }
             medicalDtl.refno = payInit.refno
-            medicalDtl.shopaccno = shopaccno
+            medicalDtl.shopaccno = hospital.shopaccno
         }
         medicalDtl.paystatus = MedicalConstant.DTL_STATUS_WIP
         medicalDtlDao.save(medicalDtl)
@@ -517,7 +523,7 @@
                 //  查询确认结果
                 val queryResult = transactionProxy.queryDtlResult(QueryDtlResultParam().apply {
                     this.refno = medicalDtl.refno
-                    this.shopaccno = systemUtilService.getBusinessValue(PortalConstant.SYSPARA_MEDICAL_SHOPACCNO)
+                    this.shopaccno = medicalDtl.shopaccno
                 })
                 if (queryResult.retcode == MedicalConstant.PAYAPI_SUCCESS_RETCODE) {
                     when (queryResult.status) {
@@ -567,7 +573,7 @@
         }
         val queryResult = transactionProxy.queryDtlResult(QueryDtlResultParam().apply {
             this.refno = medicalDtl.refno
-            this.shopaccno = systemUtilService.getBusinessValue(PortalConstant.SYSPARA_MEDICAL_SHOPACCNO)
+            this.shopaccno = medicalDtl.shopaccno
         })
         if (queryResult.retcode == MedicalConstant.PAYAPI_SUCCESS_RETCODE) {
             when (queryResult.status) {
@@ -811,11 +817,12 @@
     override fun refundMedicalDtlConfirm(originBillNo: String, refundMedicalDtl: TBMedicalDtl): TBMedicalDtl {
         val medicalDtl = medicalDtlDao.findByBillnoForUpdate(originBillNo)
                 ?: throw MedicineException("未找到订单号为:${originBillNo}的订单,请检查订单号是否正确")
+        val datetime = systemUtilService.sysdatetime
         val payCancel = transactionProxy.payCancel(ConsumePayCancelParam().apply {
             this.refno = medicalDtl.refno
             this.shopaccno = medicalDtl.shopaccno
-            this.transdate = refundMedicalDtl.transdate
-            this.transtime = refundMedicalDtl.transtime
+            this.transdate = datetime.hostdate
+            this.transtime = datetime.hosttime
             this.requestbillno = refundMedicalDtl.billno
         })
         when (payCancel.retcode) {
@@ -897,6 +904,96 @@
         }
     }
 
+    override fun refundQuickPayConfirm(originBillNo: String, refundMedicalDtl: TBMedicalDtl): TBMedicalDtl {
+        val medicalDtl = medicalDtlDao.findByBillnoForUpdate(originBillNo)
+                ?: throw MedicineException("未找到订单号为:${originBillNo}的订单,请检查订单号是否正确")
+        val datetime = systemUtilService.sysdatetime
+        val payCancel = consumePropxy.ynrccPayRefund(YnrccPayRefundParam().apply {
+            this.refno = medicalDtl.refno
+            this.refundAmount = MoneyUtil.YuanToFen(medicalDtl.mergingsubtotal)
+            this.requestbillno = refundMedicalDtl.billno
+            this.transdate = datetime.hostdate
+            this.transtime = datetime.hosttime
+        })
+        when (payCancel.retcode) {
+            MedicalConstant.PAYAPI_SUCCESS_RETCODE -> {
+                refundMedicalDtl.paystatus = MedicalConstant.DTL_STATUS_SUCCESS
+                refundMedicalDtl.accdate = refundMedicalDtl.transdate
+                refundMedicalDtl.refno = payCancel.refundRefno
+
+                medicalDtl.reverseflag = TradeDict.REVERSE_FLAG_REFUND
+                medicalDtlDao.save(medicalDtl)
+                return medicalDtlDao.save(refundMedicalDtl)
+            }
+            //  返回结果为待查询
+            MedicalConstant.PAYAPI_WAIT_QUERY -> {
+                //  查询退费结果
+                val queryResult = transactionProxy.queryDtlResult(QueryDtlResultParam().apply {
+                    this.refno = payCancel.refundRefno
+                    this.billno = refundMedicalDtl.billno
+                    this.shopaccno = medicalDtl.shopaccno
+                })
+                if (queryResult.retcode == MedicalConstant.PAYAPI_SUCCESS_RETCODE) {
+                    when (queryResult.status) {
+                        MedicalConstant.DTL_STATUS_SUCCESS -> {
+                            refundMedicalDtl.paystatus = MedicalConstant.DTL_STATUS_SUCCESS
+                            refundMedicalDtl.accdate = systemUtilService.sysdatetime.hostdate
+                            refundMedicalDtl.refno = queryResult.refno
+
+                            medicalDtl.reverseflag = TradeDict.REVERSE_FLAG_REFUND
+                            medicalDtlDao.save(medicalDtl)
+                            return medicalDtlDao.save(refundMedicalDtl)
+                        }
+                        MedicalConstant.DTL_STATUS_FAIL -> {
+                            logger.error("向payapi退费失败:[${queryResult.retmsg}]")
+                            refundMedicalDtl.paystatus = MedicalConstant.DTL_STATUS_FAIL
+                            refundMedicalDtl.remark = queryResult.retmsg
+                            refundMedicalDtl.refno = queryResult.refno
+
+                            medicalDtl.reverseflag = TradeDict.REVERSE_FLAG_NONE
+                            medicalDtlDao.save(medicalDtl)
+                            return medicalDtlDao.save(refundMedicalDtl)
+                        }
+                        MedicalConstant.DTL_STATUS_INIT -> {
+                            refundMedicalDtl.paystatus = MedicalConstant.DTL_STATUS_WIP
+                            refundMedicalDtl.refno = queryResult.refno
+
+                            medicalDtl.reverseflag = TradeDict.REVERSE_FLAG_WIP
+                            medicalDtlDao.save(medicalDtl)
+                            return medicalDtlDao.save(refundMedicalDtl)
+                        }
+                    }
+                }
+                logger.error("查询退费结果失败:${queryResult.retcode},${queryResult.retmsg}")
+                refundMedicalDtl.paystatus = MedicalConstant.DTL_STATUS_WIP
+                refundMedicalDtl.refno = queryResult.refno
+
+                medicalDtl.reverseflag = TradeDict.REVERSE_FLAG_WIP
+                medicalDtlDao.save(medicalDtl)
+                return medicalDtlDao.save(refundMedicalDtl)
+            }
+            MedicalConstant.PAYAPI_DEAL_ERROR -> {
+                refundMedicalDtl.paystatus = MedicalConstant.DTL_STATUS_FAIL
+                refundMedicalDtl.remark = payCancel.retmsg
+                refundMedicalDtl.refno = payCancel.refundRefno
+
+                medicalDtl.reverseflag = TradeDict.REVERSE_FLAG_NONE
+                medicalDtlDao.save(medicalDtl)
+                return medicalDtlDao.save(refundMedicalDtl)
+            }
+            else -> {
+                logger.error("未知的退费状态码:${payCancel.retcode},${payCancel.retmsg}")
+                refundMedicalDtl.paystatus = MedicalConstant.DTL_STATUS_FAIL
+                refundMedicalDtl.remark = payCancel.retmsg
+                refundMedicalDtl.refno = payCancel.refundRefno
+
+                medicalDtl.reverseflag = TradeDict.REVERSE_FLAG_NONE
+                medicalDtlDao.save(medicalDtl)
+                return medicalDtlDao.save(refundMedicalDtl)
+            }
+        }
+    }
+
     override fun queryRefundResult(bean: PayRefundRequestBean): TBMedicalDtl {
         val medicalDtl = medicalDtlDao.findByBillnoForUpdate(bean.outOrderNumber)
                 ?: throw MedicineException("未找到订单号为:${bean.outOrderNumber}的订单,请检查订单号是否正确")
@@ -970,19 +1067,100 @@
         return hospitalDao.findByHospitalcodeAndStatus(code, TradeDict.STATUS_NORMAL)
     }
 
-    override fun medicalYnrccPayInit(bean: YnrccPayRequestBean) {
-        val bankCardResponse = userProxy.queryCard(QueryCardParam().apply {
+    override fun medicalYnrccPayInit(bean: YnrccPayInitRequestBean): YnrccPayOrderResponse {
+        val medicalDtl = medicalDtlDao.findByBillnoForUpdate(bean.billno)
+                ?: throw MedicineException("未找到该订单")
+        val hospital = hospitalDao.findByHospitalcodeAndStatus(medicalDtl.organizationid, TradeDict.STATUS_NORMAL)
+                ?: throw MedicineException("未找到收款医院")
+        val payInit = consumePropxy.ynrccPayOrder(YnrccPayOrderParam().apply {
             this.userid = bean.userid
-            this.cardtype = ConstantUtil.CARDTYPE_BANKCARD
-        })
-        if (bankCardResponse.retcode != MedicalConstant.PAYAPI_SUCCESS_RETCODE) {
-            logger.error { "查询用户[${bean.userid}]银行卡失败:${bankCardResponse.retmsg}" }
-            throw MedicineException("支付失败:查询用户银行卡失败")
-        }
-        consumePropxy.ynrccPayOrder(YnrccPayOrderParam().apply {
-            this.userid = bean.userid
-            this.bankcardno = bankCardResponse.card.cardno
+            this.bankcardno = bean.bankcardno
             this.billno = bean.billno
+            this.amount = MoneyUtil.YuanToFen(medicalDtl.mergingsubtotal)
+            this.shopaccno = hospital.shopaccno
+            this.transdate = medicalDtl.transdate
+            this.transtime = medicalDtl.transtime
+            this.dtltype = MedicalConstant.DTLTYPE_MEDICAL
         })
+        if (payInit.retcode != MedicalConstant.PAYAPI_SUCCESS_RETCODE) {
+            logger.error("向payapi快捷支付初始化失败:${payInit.retcode}-${payInit.retmsg}")
+            throw MedicineException("支付失败 ${payInit.retmsg}")
+        }
+        medicalDtl.refno = payInit.refno
+        medicalDtl.shopaccno = hospital.shopaccno
+        medicalDtl.paystatus = MedicalConstant.DTL_STATUS_WIP
+        medicalDtlDao.save(medicalDtl)
+        return payInit
+    }
+
+    override fun medicalYnrccPayConfirm(bean: YnrccPayConfirmRequestBean): TBMedicalDtl {
+        val medicalDtl = medicalDtlDao.findByBillnoForUpdate(bean.billno)
+                ?: throw MedicineException("未找到该订单")
+        if (medicalDtl.uid != bean.uid) {
+            throw MedicineException("未找到该订单")
+        }
+        when (medicalDtl.paystatus) {
+            MedicalConstant.DTL_STATUS_INIT -> {
+                throw MedicineException("订单状态异常")
+            }
+            TradeDict.DTL_STATUS_SUCCESS -> {
+                return medicalDtl
+            }
+            TradeDict.DTL_STATUS_FAIL -> {
+                return medicalDtl
+            }
+        }
+        val payConfirm = consumePropxy.ynrccPayConfirm(YnrccPayConfirmParam().apply {
+            this.refno = medicalDtl.refno
+            this.plain = bean.plain
+            this.signature = bean.signature
+        })
+        when (payConfirm.retcode) {
+            MedicalConstant.PAYAPI_SUCCESS_RETCODE -> {
+                medicalDtl.paystatus = MedicalConstant.DTL_STATUS_SUCCESS
+                medicalDtl.accdate = systemUtilService.sysdatetime.hostdate
+                return medicalDtlDao.save(medicalDtl)
+            }
+            //  确认返回结果为待查询
+            MedicalConstant.PAYAPI_WAIT_QUERY -> {
+                //  查询确认结果
+                val queryResult = transactionProxy.queryDtlResult(QueryDtlResultParam().apply {
+                    this.refno = medicalDtl.refno
+                })
+                if (queryResult.retcode == MedicalConstant.PAYAPI_SUCCESS_RETCODE) {
+                    when (queryResult.status) {
+                        MedicalConstant.DTL_STATUS_SUCCESS -> {
+                            medicalDtl.paystatus = MedicalConstant.DTL_STATUS_SUCCESS
+                            medicalDtl.accdate = systemUtilService.sysdatetime.hostdate
+                            return medicalDtlDao.save(medicalDtl)
+                        }
+                        MedicalConstant.DTL_STATUS_FAIL -> {
+                            logger.error("向payapi扣费确认失败:[${queryResult.retmsg}]")
+                            medicalDtl.paystatus = MedicalConstant.DTL_STATUS_FAIL
+                            medicalDtl.remark = queryResult.retmsg
+                            return medicalDtlDao.save(medicalDtl)
+                        }
+                        MedicalConstant.DTL_STATUS_INIT -> {
+                            medicalDtl.paystatus = MedicalConstant.DTL_STATUS_WIP
+                            return medicalDtlDao.save(medicalDtl)
+                        }
+                    }
+                }
+                logger.error("查询确认结果失败:${queryResult.retcode},${queryResult.retmsg}")
+                medicalDtl.paystatus = MedicalConstant.DTL_STATUS_WIP
+                return medicalDtlDao.save(medicalDtl)
+            }
+            MedicalConstant.PAYAPI_DEAL_ERROR -> {
+                medicalDtl.paystatus = MedicalConstant.DTL_STATUS_FAIL
+                medicalDtl.remark = payConfirm.retmsg
+                return medicalDtlDao.save(medicalDtl)
+            }
+            else -> {
+                logger.error("未知的确认状态码:${payConfirm.retcode},${payConfirm.retmsg}")
+                medicalDtl.paystatus = MedicalConstant.DTL_STATUS_FAIL
+                medicalDtl.remark = payConfirm.retmsg
+                return medicalDtlDao.save(medicalDtl)
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
index 11ed1cd..b9c716f 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
@@ -430,6 +430,44 @@
     }
 
     /**
+     * 快捷支付HIS调用退款接口
+     */
+    @PostMapping("medical/quickpay/refund")
+    fun medicalQuickPayRefund(@RequestBody bean: PayRefundRequestBean): JsonResult? {
+        try {
+            if (bean.outOrderNumber.isEmpty() || bean.agtOrderNumber.isEmpty()) {
+                return JsonResult.error("交易号和流水号不能为空")
+            }
+            if (bean.refundNumber.isNullOrEmpty()) {
+                return JsonResult.error("退款流水号不能为空")
+            }
+            val refundToken = systemUtilService.getBusinessValue(PortalConstant.SYSPARA_MEDICAL_REFUND_TOKEN)
+            if (refundToken != bean.token) {
+                return JsonResult.error("token检验失败,请检查是否正确")
+            }
+            val refundMedicalDtl = medicalService.refundMedicalDtlInit(bean)
+            val medicalDtl = medicalService.refundQuickPayConfirm(bean.outOrderNumber, refundMedicalDtl)
+            val map = HashMap<String,String>()
+            val jsonResult = JsonResult.ok()
+            if (medicalDtl.paystatus == MedicalConstant.DTL_STATUS_SUCCESS) {
+                map["refundNumber"] = medicalDtl.hisrefundno
+            } else if (medicalDtl.paystatus == MedicalConstant.DTL_STATUS_FAIL) {
+                jsonResult.setMessage(medicalDtl.remark)
+            }
+            map["status"] = medicalDtl.paystatus
+            map["transdate"] = medicalDtl.transdate
+            map["transtime"] = medicalDtl.transtime
+            return jsonResult.put("data",map)
+        } catch (e: Exception) {
+            logger.error("系统异常", e)
+            if (e is MedicineException) {
+                return JsonResult.error(e.message)
+            }
+            return JsonResult.error("系统异常,请稍后重试")
+        }
+    }
+
+    /**
      * HIS查询退款结果
      */
     @PostMapping("medical/pay/refund/query")
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ArticleServiceImpl.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ArticleServiceImpl.kt
index 31e3abd..b7cec80 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ArticleServiceImpl.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ArticleServiceImpl.kt
@@ -39,11 +39,15 @@
     val logger = KotlinLogging.logger { }
 
     override fun saveArticle(article: TBArticle) {
-        if (article.status != null && article.status != PortalConstant.ARTICLE_STATUS_SAVE) {
+        // 发布后也可直接修改
+        if (article.status != null && article.status != PortalConstant.ARTICLE_STATUS_SAVE
+                && article.status != PortalConstant.ARTICLE_STATUS_RELEASED) {
             throw RuntimeException("文章状态异常")
         }
         val timestamp = systemUtilService.sysdatetime.sysdate
-        article.status = PortalConstant.ARTICLE_STATUS_SAVE
+        if (article.status != PortalConstant.ARTICLE_STATUS_RELEASED) {
+            article.status = PortalConstant.ARTICLE_STATUS_SAVE
+        }
         saveArticle(article, timestamp)
     }
 
diff --git a/frontend/src/views/article/components/ArticleDetail.vue b/frontend/src/views/article/components/ArticleDetail.vue
index 9496019..dcb2358 100644
--- a/frontend/src/views/article/components/ArticleDetail.vue
+++ b/frontend/src/views/article/components/ArticleDetail.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="createPost-container">
     <el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
-      <sticky v-if="action==='create'||action==='edit'" :z-index="10" :class-name="'sub-navbar draft'">
+      <sticky v-if="action==='create'||action==='edit'||action==='examine'" :z-index="10" :class-name="'sub-navbar draft'">
         <el-button
           v-if="currentcolumn && currentcolumn.needreview === '1'"
           v-loading="loading"