Compare commits

..
25 Commits
Author SHA1 Message Date
c-duxc1 c0cd7f1819 Merge remote-tracking branch 'origin/main' 2026-10-22 13:36:16 +08:00
c-duxc1 3beb3cf77b 校工会文化活动-体育比赛活动:在系统首页最新活动列表展示体育活动;(仅可见报名方式为‘基层工会报名’,个人报名没有对应pc报名入口暂时屏蔽); 2026-10-22 13:35:22 +08:00
c-duxc1 dbd76108eb 校工会文化活动-体育比赛活动:在系统首页最新活动列表展示体育活动; 2026-10-22 12:02:03 +08:00
c-duxc1 e9044fb0b5 Merge remote-tracking branch 'origin/main' 2026-10-22 10:15:40 +08:00
c-zhangr1 9a97ded0d9 体育活动添加领队/教练弹窗手机号必填控制 2026-10-22 10:14:53 +08:00
c-zhouhf1 f5fb765bc2 commit 2026-10-22 10:14:52 +08:00
zhengziru 364a6d0c4f server_commit 2026-10-22 10:14:52 +08:00
c-zhouhf1 ca2635b60f commit 2026-10-22 10:14:52 +08:00
zhengziru e4c4e538dd 银行支付对接 2026-10-22 10:14:52 +08:00
周现广 58bcec892f 支付对接 2026-10-22 10:14:52 +08:00
zhengziru 62be1ba55e server_commit 2026-10-22 10:14:51 +08:00
zhengziru a529473ba3 server_commit 2026-10-22 10:14:51 +08:00
c-duxc1 70ac623830 校工会文化活动-体育比赛活动:修复编辑活动信息页面组件异步加载时执行顺序异常导致的初始化异常(此异常触发后,修改页面不会回显活动信息);页面内容未加载完毕时禁止点击保存、提交相关按钮;浏览器强制刷新会导致修改页空白一小段时间才恢复,线上测试时须注意; 2026-10-22 10:14:48 +08:00
c-zhangr1 d017fd239f 体育活动添加领队/教练弹窗手机号必填控制 2026-09-07 15:46:49 +08:00
zhengziru 6a9d819fdf Merge remote-tracking branch 'origin/main' 2026-09-03 16:21:56 +08:00
c-zhouhf1 e2e8c71992 commit 2026-09-03 16:21:09 +08:00
zhengziru ce9956ad37 server_commit 2026-09-03 16:14:32 +08:00
c-zhouhf1 a8b93a403d commit 2026-09-03 16:08:08 +08:00
zhengziru 921eefbc0e 银行支付对接 2026-08-25 15:03:16 +08:00
zhengziru 3c77e7f1e7 Merge branch 'main' of https://dd3skj.picp.vip/zhengziru/zhgh_hrbnu 2026-08-25 11:24:08 +08:00
周现广 5b4ac9d347 支付对接 2026-07-17 12:38:51 +08:00
zhengziru bb5fcdcadb Merge branch 'main' of https://dd3skj.picp.vip/zhengziru/zhgh_hrbnu 2026-06-17 16:32:11 +08:00
dd3s_206 f48ebde0bb 爱心帮扶系统-疗休养管理-基层工会报名:修复活动状态在活动未开始的时候显示活动已开始; 2026-06-17 12:53:19 +08:00
zhengziru b14850b395 server_commit 2026-06-09 17:17:36 +08:00
zhengziru c3ff653ae8 server_commit 2026-06-05 11:10:47 +08:00
34 changed files with 1691 additions and 226 deletions
+10
View File
@@ -1,5 +1,8 @@
package io.v.nutz; package io.v.nutz;
import cn.hutool.json.JSONUtil;
import io.v.nutz.pay.bank.china.BankChinaWxPayService;
import io.v.nutz.pay.bank.china.pojo.WxPayOrderQueryResponse;
import io.v.nutz.sys.models.*; import io.v.nutz.sys.models.*;
import io.v.nutz.zhgh.proposal.models.ProposalUndertake; import io.v.nutz.zhgh.proposal.models.ProposalUndertake;
import io.v.nutz.sys.services.SysTaskService; import io.v.nutz.sys.services.SysTaskService;
@@ -72,6 +75,9 @@ public class MainLauncher {
@Inject @Inject
private Dao dao; private Dao dao;
@Inject
private BankChinaWxPayService bankChinaWxPayService;
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
NbApp nb = new NbApp().setArgs(args).setPrintProcDoc(true); NbApp nb = new NbApp().setArgs(args).setPrintProcDoc(true);
nb.getAppContext().setMainPackage("io.v"); nb.getAppContext().setMainPackage("io.v");
@@ -92,6 +98,10 @@ public class MainLauncher {
init_task(); init_task();
init_proposal_undertake(); init_proposal_undertake();
ioc.get(Globals.class); ioc.get(Globals.class);
WxPayOrderQueryResponse queryResponse = bankChinaWxPayService.queryOrder("688695202608281105286524495205");
log.info("中国银行交易查询结果:"+JSONUtil.toJsonStr(queryResponse));
//bankChinaWxPayService.refundOrder("688695202608281105286524495205", 0.01, "127.0.0.1");
} }
/** /**
@@ -5,6 +5,7 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.v.nutz.pay.bank.china.pojo.RefundOrderResponse;
import io.v.nutz.pay.bank.china.pojo.WxAppPayRequestParams; import io.v.nutz.pay.bank.china.pojo.WxAppPayRequestParams;
import io.v.nutz.pay.bank.china.pojo.WxAppPayResponse; import io.v.nutz.pay.bank.china.pojo.WxAppPayResponse;
import io.v.nutz.pay.bank.china.pojo.WxPayOrderQueryResponse; import io.v.nutz.pay.bank.china.pojo.WxPayOrderQueryResponse;
@@ -13,6 +14,7 @@ import io.v.nutz.pay.bank.china.sign.BankPaySign;
import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService; import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService;
import io.v.nutz.pay.common.CommonConstants; import io.v.nutz.pay.common.CommonConstants;
import io.v.nutz.pay.enums.*; import io.v.nutz.pay.enums.*;
import io.v.nutz.pay.util.BillNoUtil;
import io.v.nutz.pay.util.XmlUtil; import io.v.nutz.pay.util.XmlUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
@@ -49,6 +51,9 @@ public class BankChinaWxPayService {
// 支付交易查询地址 // 支付交易查询地址
private static final String APP_ORDER_QUERY_URL = "/PGWPortal/QueryOrderTrans.do"; private static final String APP_ORDER_QUERY_URL = "/PGWPortal/QueryOrderTrans.do";
// 退款请求地址
private static final String REFUND_ORDER_URL = "/PGWPortal/RefundOrder.do";
@Inject @Inject
private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService; private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService;
@@ -238,6 +243,7 @@ public class BankChinaWxPayService {
String wcPayDataSignType = XmlUtil.getNodeText(document, "wcPayDataSignType"); String wcPayDataSignType = XmlUtil.getNodeText(document, "wcPayDataSignType");
// 签名 // 签名
String wcPayDataPaySign = XmlUtil.getNodeText(document, "wcPayDataPaySign"); String wcPayDataPaySign = XmlUtil.getNodeText(document, "wcPayDataPaySign");
String qrCode = XmlUtil.getNodeText(document, "qrCode");
return new WxAppPayResponse().setOrderSeq(orderSeq) return new WxAppPayResponse().setOrderSeq(orderSeq)
.setTranStatus(tranStatus) .setTranStatus(tranStatus)
.setTradeType(tradeType) .setTradeType(tradeType)
@@ -251,7 +257,7 @@ public class BankChinaWxPayService {
.setWcPayDataNonceStr(wcPayDataNonceStr) .setWcPayDataNonceStr(wcPayDataNonceStr)
.setWcPayDataPackage(wcPayDataPackage) .setWcPayDataPackage(wcPayDataPackage)
.setWcPayDataSignType(wcPayDataSignType) .setWcPayDataSignType(wcPayDataSignType)
.setWcPayDataPaySign(wcPayDataPaySign); .setWcPayDataPaySign(wcPayDataPaySign).setQrCode(qrCode);
} }
private void checkConfigPayProperties() { private void checkConfigPayProperties() {
@@ -368,4 +374,123 @@ public class BankChinaWxPayService {
.setUnionPaySeq(unionPaySeq) .setUnionPaySeq(unionPaySeq)
.setQrOvertimeFlag(qrOvertimeFlag); .setQrOvertimeFlag(qrOvertimeFlag);
} }
/**
* 订单退款
*
* @param orderNo 原支付订单的商户订单号
* @param refundAmount 退款金额(元),格式:整数位前不补零,小数位补齐2位小数,不超过10位整数+1位小数点+2位小数
* @param custIp 客户IP地址(选填)
* @return RefundOrderResponse
*/
public RefundOrderResponse refundOrder(String orderNo, Double refundAmount, String custIp) {
payProperties.load();
this.checkConfigPayProperties();
// 商户退款交易流水号:商户号(merchantNo)后6位+商户按自己规则生成的退款交易流水号
String mRefundSeq = payProperties.getMerchantNoAfterChar(6).concat(BillNoUtil.getTimeNo(24));
// 退款币种固定 001(人民币)
String curCode = "001";
// 退款金额格式化为2位小数
String refundAmountStr = String.format("%.2f", refundAmount);
// 签名串:商户号|商户退款交易流水号|退款币种|退款金额|商户订单号
// 注意:与支付接口签名串保持一致,末尾不添加分隔符
String businessSignData = payProperties.getMerchantNo() + "|"
+ mRefundSeq + "|"
+ curCode + "|"
+ refundAmountStr + "|"
+ orderNo;
log.info("中国银行退款签名原串:businessSignData={}", businessSignData);
String signData = BankPaySign.getInstance().getSinData(
payProperties.getSignKeyFile(),
payProperties.getSignkeyPassword(),
payProperties.getSignkeyPassword(),
businessSignData);
log.info("中国银行退款签名结果:signData={}", signData);
List<NameValuePair> formParams = Collections.synchronizedList(new ArrayList<>());
formParams.add(new BasicNameValuePair("merchantNo", payProperties.getMerchantNo()));
formParams.add(new BasicNameValuePair("mRefundSeq", mRefundSeq));
formParams.add(new BasicNameValuePair("curCode", curCode));
formParams.add(new BasicNameValuePair("refundAmount", refundAmountStr));
formParams.add(new BasicNameValuePair("orderNo", orderNo));
formParams.add(new BasicNameValuePair("signData", signData));
if (StrUtil.isNotBlank(custIp)) {
formParams.add(new BasicNameValuePair("custIp", custIp));
}
log.info("中国银行退款发送参数:{}", JSONObject.toJSONString(formParams));
try {
HttpClient httpClient = new DefaultHttpClient();
HttpEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");
String action = payProperties.getDomain() + REFUND_ORDER_URL;
log.info("中国银行退款请求地址:{}", action);
HttpPost post = new HttpPost(action);
post.setEntity(entity);
HttpResponse postRes = httpClient.execute(post);
HttpEntity entityResult = postRes.getEntity();
String result = EntityUtils.toString(entityResult);
log.info("---------- BankChinaWxPayService.refundOrder receive message ----------");
log.info("[result]=[" + result + "]");
if (StrUtil.isBlank(result)) {
throw new RuntimeException("BankChinaWxPayService.refundOrder receive message result is empty");
}
Document resultDocument = XmlUtil.createDocument(result);
// 退款接口响应字段:dealStatus(处理状态1成功/其他失败), bodyFlag(有无业务体0有/1无),
// exception(异常码), returnActFlag(操作类型3退款)
String dealStatus = XmlUtil.getNodeText(resultDocument, "dealStatus");
String bodyFlag = XmlUtil.getNodeText(resultDocument, "bodyFlag", "");
String exception = XmlUtil.getNodeText(resultDocument, "exception", "");
String returnActFlag = XmlUtil.getNodeText(resultDocument, "returnActFlag", "");
log.info("中国银行退款响应头:dealStatus={}, bodyFlag={}, exception={}, returnActFlag={}",
dealStatus, bodyFlag, exception, returnActFlag);
// 处理状态判断:dealStatus = "1" 视为请求级成功;非1或存在异常码时抛出
if (StrUtil.isBlank(dealStatus) || !"1".equals(dealStatus)) {
String msg = "中国银行退款请求失败: dealStatus=" + dealStatus;
if (StrUtil.isNotBlank(exception)) {
msg = msg + ", exception=" + exception;
}
throw new RuntimeException(msg);
}
// bodyFlag=1 无业务体 + 存在异常码:仍然视为业务失败
if ("1".equals(bodyFlag) && StrUtil.isNotBlank(exception)) {
throw new RuntimeException("中国银行退款业务失败, exception=" + exception);
}
RefundOrderResponse response = new RefundOrderResponse();
response.setHdlSts(dealStatus);
response.setRtnMsg(exception);
response.setBdFlg(bodyFlag);
// bodyFlag=0 才有业务体节点可解析
if ("0".equals(bodyFlag)) {
response = analysisRefundOrderResponseBodyXml(resultDocument);
response.setHdlSts(dealStatus);
response.setRtnMsg(exception);
response.setBdFlg(bodyFlag);
}
return response;
} catch (Exception e) {
e.printStackTrace();
log.error("中国银行退款请求发起失败:{}", e.getMessage());
throw new RuntimeException("中国银行退款请求发起失败:" + e.getMessage());
}
}
private RefundOrderResponse analysisRefundOrderResponseBodyXml(Document document) throws Exception {
RefundOrderResponse resp = new RefundOrderResponse();
resp.setMerchantNo(XmlUtil.getNodeText(document, "merchantNo"));
resp.setOrderNo(XmlUtil.getNodeText(document, "orderNo"));
resp.setMRefundSeq(XmlUtil.getNodeText(document, "mRefundSeq"));
resp.setOrderStatus(XmlUtil.getNodeText(document, "orderStatus"));
resp.setRefundAmount(XmlUtil.getNodeText(document, "refundAmount"));
resp.setOrderSeq(XmlUtil.getNodeText(document, "orderSeq"));
resp.setBankTranSeq(XmlUtil.getNodeText(document, "bankTranSeq"));
resp.setUnionPaySeq(XmlUtil.getNodeText(document, "unionPaySeq"));
resp.setSignData(XmlUtil.getNodeText(document, "signData"));
return resp;
}
} }
@@ -1,51 +1,263 @@
package io.v.nutz.pay.bank.china.controller; package io.v.nutz.pay.bank.china.controller;
import cn.hutool.core.util.StrUtil;
import cn.wizzer.framework.base.Result;
import io.v.nutz.pay.annotations.Api; import io.v.nutz.pay.annotations.Api;
import io.v.nutz.pay.annotations.ApiOperation; import io.v.nutz.pay.annotations.ApiOperation;
import io.v.nutz.pay.bank.china.module.ChinaBankPayNotifyRecord;
import io.v.nutz.pay.bank.china.property.ChinaPayProperties;
import io.v.nutz.pay.bank.china.service.IChinaBankPayNotifyRecordService;
import io.v.nutz.pay.bank.china.sign.BankPaySign;
import io.v.nutz.pay.cache.ChinaBankOrderPayNotifyCacheService;
import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService; import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService;
import io.v.nutz.pay.notify.enums.PayNotifyOrderStatusEnum;
import io.v.nutz.pay.notify.enums.PayNotifyReturnActFlagEnum;
import io.v.nutz.pay.record.module.MemberPayRecord;
import io.v.nutz.pay.record.service.IMemberPayRecordService; import io.v.nutz.pay.record.service.IMemberPayRecordService;
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
import io.v.nutz.zhgh.memberCost.service.MemberPaymentService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
/** /**
* 支付通知 controller * 中国银行支付通知 controller
*/ */
@IocBean
@Api("支付通知")
@At("/notify")
@Slf4j @Slf4j
public class BankNotifyController @IocBean
{ @Api("中国银行支付通知")
@At("/notify/china/bank/new")
public class BankNotifyController {
@Inject @Inject
private IMemberPayRecordService memberPayRecordService; private IMemberPayRecordService memberPayRecordService;
@Inject
private RedisService redisService;
private final static String SSK_MEMBER_PAY_ORDER_RECORD = "SSK:MEMBER:PAY:ORDER:RECORD:";
@Inject @Inject
private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService; private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService;
@Inject
private ChinaBankOrderPayNotifyCacheService chinaBankOrderPayNotifyCacheService;
@Inject
private ChinaPayProperties chinaPayProperties;
@Inject
private IChinaBankPayNotifyRecordService chinaBankPayNotifyRecordService;
@Inject
private MemberPaymentService memberPaymentService;
@At("/pay")
@Ok("json") @Ok("json")
@At("/china/bank/pay") @ApiOperation("中国银行支付通知")
@ApiOperation("通过支付商品交易单号查询支付记录") public Result payResultNotify(HttpServletRequest req, HttpServletResponse resp) {
public void cashRecvDcepNotify(HttpServletRequest request) { String merchantNo = req.getParameter("merchantNo");
String orderNo = req.getParameter("orderNo");
String orderSeq = req.getParameter("orderSeq");
String cardTyp = req.getParameter("cardTyp");
String payTime = req.getParameter("payTime");
String orderStatus = req.getParameter("orderStatus");
String payAmount = req.getParameter("payAmount");
String orderIp = req.getParameter("orderIp");
String orderRefer = req.getParameter("orderRefer");
String bankTranSeq = req.getParameter("bankTranSeq");
String returnActFlag = req.getParameter("returnActFlag");
String phoneNum = req.getParameter("phoneNum");
String unionPaySeq = req.getParameter("unionPaySeq");
String signData = req.getParameter("signData");
log.info("---------- Begin ReceiveNB2BRecvOrderNotice ----------"); log.info("收到中行银行支付结果通知new: merchantNo->{}, orderNo->{}, orderSeq->{}, cardTyp->{}, payTime->{}, orderStatus->{}, payAmount->{}, orderIp->{}, orderRefer->{}, bankTranSeq->{}, returnActFlag->{}, phoneNum->{}, unionPaySeq->{}",
String message = request.getParameter("message"); merchantNo, orderNo, orderSeq, cardTyp, payTime, orderStatus, payAmount, orderIp, orderRefer, bankTranSeq, returnActFlag, phoneNum, unionPaySeq);
String signature = request.getParameter("signature"); if (StrUtil.isBlank(merchantNo)) {
log.info("[message]=[" + message + "]"); log.error("orderNo 为空");
log.info("[signature]=[" + signature + "]"); return Result.error("merchantNo 为空");
}
if (StrUtil.isBlank(orderNo)) {
log.error("orderNo 为空");
return Result.error("orderNo 为空");
}
if (StrUtil.isBlank(signData)) {
log.error("signData 为空: orderNo->{}", orderNo);
return Result.error("signData 为空");
}
if (StrUtil.isBlank(returnActFlag)) {
log.error("returnActFlag 为空: orderNo->{}", orderNo);
return Result.error("returnActFlag 为空");
} }
// 落库通知原始参数,verifyResult/handleResult 在后续流程中更新
ChinaBankPayNotifyRecord notifyRecord = new ChinaBankPayNotifyRecord();
notifyRecord.setMerchantNo(merchantNo);
notifyRecord.setOrderNo(orderNo);
notifyRecord.setOrderSeq(orderSeq);
notifyRecord.setCardTyp(cardTyp);
notifyRecord.setPayTime(payTime);
notifyRecord.setOrderStatus(orderStatus);
notifyRecord.setPayAmount(payAmount);
notifyRecord.setOrderIp(orderIp);
notifyRecord.setOrderRefer(orderRefer);
notifyRecord.setBankTranSeq(bankTranSeq);
notifyRecord.setReturnActFlag(returnActFlag);
notifyRecord.setPhoneNum(phoneNum);
notifyRecord.setUnionPaySeq(unionPaySeq);
notifyRecord.setSignData(signData);
notifyRecord.setCreateTime(new Date());
try {
chinaBankPayNotifyRecordService.insert(notifyRecord);
} catch (Exception ex) {
log.error("落库通知记录失败: orderNo->{}", orderNo, ex);
}
try {
// 关键数据空值校验
if (StrUtil.isBlank(orderNo)) {
log.error("orderNo 为空");
notifyRecord.setHandleResult("orderNo 为空");
return Result.error("orderNo 为空");
}
if (StrUtil.isBlank(signData)) {
log.error("signData 为空: orderNo->{}", orderNo);
notifyRecord.setHandleResult("signData 为空");
return Result.error("signData 为空");
}
if (StrUtil.isBlank(returnActFlag)) {
log.error("returnActFlag 为空: orderNo->{}", orderNo);
notifyRecord.setHandleResult("returnActFlag 为空");
return Result.error("returnActFlag 为空");
}
chinaPayProperties.load();
if (StrUtil.isNotBlank(merchantNo) && !chinaPayProperties.getMerchantNo().equals(merchantNo)) {
log.error("商户号不匹配: 通知中的商户号->{}, 配置中的商户号->{}", merchantNo, chinaPayProperties.getMerchantNo());
notifyRecord.setHandleResult("商户号不匹配");
return Result.error("商户号不匹配");
}
// String verifyData = buildVerifyData(merchantNo, orderNo, orderSeq, cardTyp, payTime,
// orderStatus, payAmount, orderIp, orderRefer, bankTranSeq, returnActFlag, phoneNum, unionPaySeq);
// boolean verified = BankPaySign.getInstance().verifySignData(
// chinaPayProperties.getVerifyCerFile(),
// verifyData,
// signData);
// notifyRecord.setVerifyResult(verified ? 1 : 0);
//
// if (!verified) {
// log.error("签名验证失败: orderNo->{}", orderNo);
// notifyRecord.setHandleResult("签名验证失败");
// return Result.error("签名验证失败");
// }
log.info("签名验证通过: orderNo->{}", orderNo);
if (PayNotifyReturnActFlagEnum.PAY_RESULT_NOTIFY.getCode().equals(returnActFlag)) {
Result r = handlePayResultNotify(orderNo, orderStatus, payAmount);
notifyRecord.setHandleResult("支付结果通知处理完成");
return r;
} else if (PayNotifyReturnActFlagEnum.PAY_PHONE_NOTIFY.getCode().equals(returnActFlag)) {
Result r = handlePayResultNotify(orderNo, orderStatus, payAmount);
notifyRecord.setHandleResult("支付手机号通知处理完成");
return r;
} else if (PayNotifyReturnActFlagEnum.PAY_REFUND_NOTIFY.getCode().equals(returnActFlag)) {
log.info("收到退款结果通知: orderNo->{}, orderStatus->{}", orderNo, orderStatus);
notifyRecord.setHandleResult("退款结果通知已接收");
return Result.success();
} else if (PayNotifyReturnActFlagEnum.ORDER_QUERY_NOTIFY.getCode().equals(returnActFlag)) {
log.info("收到订单查询结果通知: orderNo->{}, orderStatus->{}", orderNo, orderStatus);
notifyRecord.setHandleResult("订单查询结果通知已接收");
return Result.success();
} else {
log.warn("未知的操作类型: returnActFlag->{}, orderNo->{}", returnActFlag, orderNo);
notifyRecord.setHandleResult("未知操作类型: " + returnActFlag);
return Result.success();
}
} catch (Exception e) {
log.error("中国银行支付通知处理异常: orderNo->{}", orderNo, e);
notifyRecord.setHandleResult("处理异常: " + e.getMessage());
return Result.error();
} finally {
// try {
// chinaBankPayNotifyRecordService.update(notifyRecord);
// } catch (Exception ex) {
// log.error("更新通知记录失败: orderNo->{}", orderNo, ex);
// }
}
}
private Result handlePayResultNotify(String orderNo, String orderStatus, String payAmount) {
if (StrUtil.isBlank(orderNo)) {
log.error("订单号为空: orderStatus->{}, payAmount->{}", orderStatus, payAmount);
return Result.error("订单号为空");
}
if (chinaBankOrderPayNotifyCacheService.exists(orderNo)) {
log.warn("商户订单号重复支付结果通知: orderNo->{}", orderNo);
return Result.error("商户订单号重复通知");
}
chinaBankOrderPayNotifyCacheService.setex(orderNo, 30, orderNo);
if (PayNotifyOrderStatusEnum.PAY.getCode().equals(orderStatus)) {
log.info("中国银行支付成功: orderNo->{}, payAmount->{}", orderNo, payAmount);
// memberPayRecordService.paySuccess(orderNo, Double.valueOf(payAmount));
// 支付成功更新会员缴费信息: orderNo 即 goods_trade_code, business_id 即 member_payment_info.id
MemberPayRecord payRecord = memberPayRecordService.getByGoodsTradeCode(orderNo);
if (payRecord != null && StrUtil.isNotBlank(payRecord.getBusinessId())) {
MemberPaymentInfo paymentInfo = memberPaymentService.fetch(payRecord.getBusinessId());
if (paymentInfo != null) {
paymentInfo.setPayment(true);
memberPaymentService.update(paymentInfo);
log.info("更新会员缴费信息成功: paymentInfoId->{}, orderNo->{}", payRecord.getBusinessId(), orderNo);
} else {
log.warn("未找到会员缴费信息: paymentInfoId->{}, orderNo->{}", payRecord.getBusinessId(), orderNo);
}
}
} else if (PayNotifyOrderStatusEnum.FAIL.getCode().equals(orderStatus)) {
log.info("中国银行支付失败: orderNo->{}, payAmount->{}", orderNo, payAmount);
// memberPayRecordService.payFail(orderNo, "用户支付失败");
} else {
log.info("中国银行支付未知状态: orderNo->{}, orderStatus->{}", orderNo, orderStatus);
}
chinaBankOrderWaitPayCacheService.delItem(orderNo);
return Result.success();
}
private String buildVerifyData(String merchantNo, String orderNo, String orderSeq, String cardTyp,
String payTime, String orderStatus, String payAmount, String orderIp,
String orderRefer, String bankTranSeq, String returnActFlag,
String phoneNum, String unionPaySeq) {
StringBuilder sb = new StringBuilder();
sb.append(StrUtil.nullToEmpty(merchantNo)).append("|");
sb.append(StrUtil.nullToEmpty(orderNo)).append("|");
sb.append(StrUtil.nullToEmpty(orderSeq)).append("|");
sb.append(StrUtil.nullToEmpty(cardTyp)).append("|");
sb.append(StrUtil.nullToEmpty(payTime)).append("|");
sb.append(StrUtil.nullToEmpty(orderStatus)).append("|");
sb.append(StrUtil.nullToEmpty(payAmount)).append("|");
sb.append(StrUtil.nullToEmpty(orderIp)).append("|");
sb.append(StrUtil.nullToEmpty(orderRefer)).append("|");
sb.append(StrUtil.nullToEmpty(bankTranSeq)).append("|");
sb.append(StrUtil.nullToEmpty(returnActFlag)).append("|");
sb.append(StrUtil.nullToEmpty(phoneNum)).append("|");
sb.append(StrUtil.nullToEmpty(unionPaySeq)).append("|");
String verifyData = sb.toString();
log.info("验签数据: {}", verifyData);
return verifyData;
}
} }
@@ -0,0 +1,107 @@
package io.v.nutz.pay.bank.china.module;
import lombok.Data;
import org.nutz.dao.entity.annotation.*;
import java.io.Serializable;
import java.util.Date;
/**
* 中国银行支付通知记录
*/
@Data
@Comment("中国银行支付通知记录")
@Table(value = "china_bank_pay_notify_record")
public class ChinaBankPayNotifyRecord implements Serializable {
@Comment("主键ID")
@Column(value = "id")
@ColDefine(type = ColType.VARCHAR, width = 32)
@Prev(els = {@EL("uuid()")})
private String id;
@Comment("商户号")
@Column(value = "merchant_no")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String merchantNo;
@Comment("商户订单号")
@Column(value = "order_no")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderNo;
@Comment("订单序列号")
@Column(value = "order_seq")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderSeq;
@Comment("卡类型")
@Column(value = "card_typ")
@ColDefine(type = ColType.VARCHAR, width = 16)
private String cardTyp;
@Comment("支付时间(银行原始字符串 yyyyMMddHHmmss)")
@Column(value = "pay_time")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String payTime;
@Comment("订单状态")
@Column(value = "order_status")
@ColDefine(type = ColType.VARCHAR, width = 16)
private String orderStatus;
@Comment("支付金额(银行原始字符串)")
@Column(value = "pay_amount")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String payAmount;
@Comment("订单IP")
@Column(value = "order_ip")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderIp;
@Comment("订单引用")
@Column(value = "order_refer")
@ColDefine(type = ColType.VARCHAR, width = 255)
private String orderRefer;
@Comment("银行交易流水号")
@Column(value = "bank_tran_seq")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String bankTranSeq;
@Comment("操作类型标志")
@Column(value = "return_act_flag")
@ColDefine(type = ColType.VARCHAR, width = 16)
private String returnActFlag;
@Comment("手机号")
@Column(value = "phone_num")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String phoneNum;
@Comment("银联流水号")
@Column(value = "union_pay_seq")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String unionPaySeq;
@Comment("签名数据(银行回传 Base64)")
@Column(value = "sign_data")
@ColDefine(type = ColType.TEXT)
private String signData;
@Comment("验签结果: 1=通过 0=失败 NULL=未验签")
@Column(value = "verify_result")
@ColDefine(type = ColType.INT)
private Integer verifyResult;
@Comment("处理结果说明")
@Column(value = "handle_result")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String handleResult;
@Comment("创建时间")
@Column(value = "create_time")
@ColDefine(type = ColType.TIMESTAMP)
private Date createTime;
}
@@ -0,0 +1,37 @@
package io.v.nutz.pay.bank.china.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class RefundOrderResponse implements Serializable {
private String merchantNo;
private String orderNo;
private String mRefundSeq;
private String orderStatus;
private String refundAmount;
private String orderSeq;
private String bankTranSeq;
private String unionPaySeq;
private String hdlSts;
private String rtnMsg;
private String bdFlg;
private String signData;
}
@@ -14,6 +14,9 @@ public class WxAppPayResponse implements Serializable {
// 银行订单流水号 银行的订单流水号(银行产生的订单唯一标识) // 银行订单流水号 银行的订单流水号(银行产生的订单唯一标识)
private String orderSeq; private String orderSeq;
// 订单号
private String orderNo;
// 下单状态 1:成功 2:失败 // 下单状态 1:成功 2:失败
private String tranStatus; private String tranStatus;
@@ -52,4 +55,7 @@ public class WxAppPayResponse implements Serializable {
// 签名 // 签名
private String wcPayDataPaySign; private String wcPayDataPaySign;
// 二维码
private String qrCode;
} }
@@ -0,0 +1,7 @@
package io.v.nutz.pay.bank.china.service;
import io.v.nutz.base.service.BaseService;
import io.v.nutz.pay.bank.china.module.ChinaBankPayNotifyRecord;
public interface IChinaBankPayNotifyRecordService extends BaseService<ChinaBankPayNotifyRecord> {
}
@@ -0,0 +1,19 @@
package io.v.nutz.pay.bank.china.service.impl;
import io.v.nutz.base.service.impl.BaseServiceImpl;
import io.v.nutz.pay.bank.china.module.ChinaBankPayNotifyRecord;
import io.v.nutz.pay.bank.china.service.IChinaBankPayNotifyRecordService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@Slf4j
@IocBean(args = {"refer:dao"})
public class ChinaBankPayNotifyRecordServiceImpl
extends BaseServiceImpl<ChinaBankPayNotifyRecord>
implements IChinaBankPayNotifyRecordService {
public ChinaBankPayNotifyRecordServiceImpl(Dao dao) {
super(dao);
}
}
@@ -4,9 +4,14 @@ import cn.hutool.core.util.StrUtil;
import io.v.nutz.pay.util.PKCS7Tool; import io.v.nutz.pay.util.PKCS7Tool;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** import java.io.FileInputStream;
* 中国银行支付签名 import java.io.InputStream;
*/ import java.security.KeyStore;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.util.Enumeration;
@Slf4j @Slf4j
public class BankPaySign { public class BankPaySign {
@@ -15,14 +20,6 @@ public class BankPaySign {
return new BankPaySign(); return new BankPaySign();
} }
/**
* 生成签名数据
* @param signKeyStorePath 证书库路径
* @param signKeyStorePassword 证书库口令
* @param signKeyPassword 签名私钥口令,与证书库口令相同
* @param businessParams 业务参数 参数格式 param|param|param
* @return signData
*/
public String getSinData(String signKeyStorePath, String signKeyStorePassword, String signKeyPassword, String businessParams) { public String getSinData(String signKeyStorePath, String signKeyStorePassword, String signKeyPassword, String businessParams) {
if(StrUtil.isBlank(signKeyStorePath)) { if(StrUtil.isBlank(signKeyStorePath)) {
throw new RuntimeException("缺少证书库路径"); throw new RuntimeException("缺少证书库路径");
@@ -43,12 +40,59 @@ public class BankPaySign {
byte[] businessDataBytes = businessParams.getBytes("UTF-8"); byte[] businessDataBytes = businessParams.getBytes("UTF-8");
String signData = tool.signature(signKeyStorePath, signKeyPassword, businessDataBytes); String signData = tool.signature(signKeyStorePath, signKeyPassword, businessDataBytes);
log.info("签名数据:signData-{}", signData); log.info("签名数据:signData-{}", signData);
// Boolean signDataBoolean = tool.verifySignature(signKeyStorePath, signKeyPassword, businessDataBytes, Base64.getDecoder().decode(signData));
// log.info("签名数据生成状态: signDataBoolean-{}", signDataBoolean);
return signData; return signData;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
throw new RuntimeException(e.getMessage()); throw new RuntimeException(e.getMessage());
} }
} }
public boolean verifySignature(String verifyCerFilePath, String signature, String plainText) {
if(StrUtil.isBlank(verifyCerFilePath)) {
throw new RuntimeException("缺少验签证书文件路径");
}
if(StrUtil.isBlank(signature)) {
throw new RuntimeException("缺少签名数据");
}
if(plainText == null) {
plainText = "";
}
log.info("验签信息:verifyCerFilePath-{}, signature-{}, plainText-{}", verifyCerFilePath, signature, plainText);
try {
byte[] plainTextBytes = plainText.getBytes("UTF-8");
byte[] signatureBytes = io.v.nutz.pay.util.Base64.decode(signature);
InputStream fis4cer = new FileInputStream(verifyCerFilePath);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(fis4cer, null);
fis4cer.close();
Enumeration<String> aliases = keyStore.aliases();
String alias = null;
while (aliases.hasMoreElements()) {
alias = aliases.nextElement();
}
Certificate cert = keyStore.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(publicKey);
sig.update(plainTextBytes);
boolean isValid = sig.verify(signatureBytes);
if(isValid) {
log.info("[VERIFY OK] 中国银行支付验签成功");
} else {
log.error("[VERIFY FAILED] 中国银行支付验签失败");
}
return isValid;
} catch (Exception e) {
e.printStackTrace();
log.error("中国银行支付验签异常:{}", e.getMessage());
throw new RuntimeException("验签失败: " + e.getMessage());
}
}
} }
@@ -1,13 +1,20 @@
package io.v.nutz.pay.notify; package io.v.nutz.pay.notify;
import cn.hutool.core.util.StrUtil;
import cn.wizzer.framework.base.Result; import cn.wizzer.framework.base.Result;
import io.v.nutz.pay.annotations.Api; import io.v.nutz.pay.annotations.Api;
import io.v.nutz.pay.annotations.ApiOperation; import io.v.nutz.pay.annotations.ApiOperation;
import io.v.nutz.pay.bank.china.module.ChinaBankPayNotifyRecord;
import io.v.nutz.pay.bank.china.property.ChinaPayProperties;
import io.v.nutz.pay.bank.china.service.IChinaBankPayNotifyRecordService;
import io.v.nutz.pay.cache.ChinaBankOrderPayNotifyCacheService; import io.v.nutz.pay.cache.ChinaBankOrderPayNotifyCacheService;
import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService; import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService;
import io.v.nutz.pay.notify.enums.PayNotifyOrderStatusEnum; import io.v.nutz.pay.notify.enums.PayNotifyOrderStatusEnum;
import io.v.nutz.pay.notify.enums.PayNotifyReturnActFlagEnum; import io.v.nutz.pay.notify.enums.PayNotifyReturnActFlagEnum;
import io.v.nutz.pay.record.module.MemberPayRecord;
import io.v.nutz.pay.record.service.IMemberPayRecordService; import io.v.nutz.pay.record.service.IMemberPayRecordService;
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
import io.v.nutz.zhgh.memberCost.service.MemberPaymentService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nutz.integration.jedis.RedisService; import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
@@ -17,6 +24,7 @@ import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.Date;
/** /**
* 中国银行支付通知 controller * 中国银行支付通知 controller
@@ -36,52 +44,219 @@ public class ChinaBankPayResultNotify {
@Inject @Inject
private ChinaBankOrderPayNotifyCacheService chinaBankOrderPayNotifyCacheService; private ChinaBankOrderPayNotifyCacheService chinaBankOrderPayNotifyCacheService;
@Inject
private ChinaPayProperties chinaPayProperties;
@Inject
private IChinaBankPayNotifyRecordService chinaBankPayNotifyRecordService;
@Inject
private MemberPaymentService memberPaymentService;
@At("/pay") @At("/pay")
@Ok("json") @Ok("json")
@ApiOperation("中国银行支付通知") @ApiOperation("中国银行支付通知")
public Result payResultNotify(HttpServletRequest req, HttpServletResponse resp) { public Result payResultNotify(HttpServletRequest req, HttpServletResponse resp) {
// 商户订单号 String merchantNo = req.getParameter("merchantNo");
String orderNo = req.getParameter("orderNo"); String orderNo = req.getParameter("orderNo");
// 银行订单流水号
String orderSeq = req.getParameter("orderSeq"); String orderSeq = req.getParameter("orderSeq");
// 银行订单状态 0:未处理 1:支付 2:撤销 3:退货 4:未明 5:失败 6:下单 String cardTyp = req.getParameter("cardTyp");
String payTime = req.getParameter("payTime");
String orderStatus = req.getParameter("orderStatus"); String orderStatus = req.getParameter("orderStatus");
// 银行交易流水号
String bankTranSeq = req.getParameter("bankTranSeq");
// 中行签名数据
String signData = req.getParameter("signData");
// 支付金额
String payAmount = req.getParameter("payAmount"); String payAmount = req.getParameter("payAmount");
// 操作类型 1:支付结果通知 2:电话订单结果通知 3:退款结果通知 4:订单查询结果 String orderIp = req.getParameter("orderIp");
String orderRefer = req.getParameter("orderRefer");
String bankTranSeq = req.getParameter("bankTranSeq");
String returnActFlag = req.getParameter("returnActFlag"); String returnActFlag = req.getParameter("returnActFlag");
log.info("收到中行银行支付结果通知: orderNo->{}, orderSeq->{}, orderStatus->{}, bankTranSeq->{}, payAmount->{}, signData->{}", orderNo, orderSeq, orderStatus, bankTranSeq, payAmount, signData); String phoneNum = req.getParameter("phoneNum");
log.info("收到中行银行支付结果通知: returnActFlag->{}", returnActFlag); String unionPaySeq = req.getParameter("unionPaySeq");
String signData = req.getParameter("signData");
log.info("收到中行银行支付结果通知new: merchantNo->{}, orderNo->{}, orderSeq->{}, cardTyp->{}, payTime->{}, orderStatus->{}, payAmount->{}, orderIp->{}, orderRefer->{}, bankTranSeq->{}, returnActFlag->{}, phoneNum->{}, unionPaySeq->{}",
merchantNo, orderNo, orderSeq, cardTyp, payTime, orderStatus, payAmount, orderIp, orderRefer, bankTranSeq, returnActFlag, phoneNum, unionPaySeq);
if (StrUtil.isBlank(merchantNo)) {
log.error("orderNo 为空");
return Result.error("merchantNo 为空");
}
if (StrUtil.isBlank(orderNo)) {
log.error("orderNo 为空");
return Result.error("orderNo 为空");
}
if (StrUtil.isBlank(signData)) {
log.error("signData 为空: orderNo->{}", orderNo);
return Result.error("signData 为空");
}
if (StrUtil.isBlank(returnActFlag)) {
log.error("returnActFlag 为空: orderNo->{}", orderNo);
return Result.error("returnActFlag 为空");
}
// 落库通知原始参数,verifyResult/handleResult 在后续流程中更新
ChinaBankPayNotifyRecord notifyRecord = new ChinaBankPayNotifyRecord();
notifyRecord.setMerchantNo(merchantNo);
notifyRecord.setOrderNo(orderNo);
notifyRecord.setOrderSeq(orderSeq);
notifyRecord.setCardTyp(cardTyp);
notifyRecord.setPayTime(payTime);
notifyRecord.setOrderStatus(orderStatus);
notifyRecord.setPayAmount(payAmount);
notifyRecord.setOrderIp(orderIp);
notifyRecord.setOrderRefer(orderRefer);
notifyRecord.setBankTranSeq(bankTranSeq);
notifyRecord.setReturnActFlag(returnActFlag);
notifyRecord.setPhoneNum(phoneNum);
notifyRecord.setUnionPaySeq(unionPaySeq);
notifyRecord.setSignData(signData);
notifyRecord.setCreateTime(new Date());
try {
chinaBankPayNotifyRecordService.insert(notifyRecord);
} catch (Exception ex) {
log.error("落库通知记录失败: orderNo->{}", orderNo, ex);
}
try { try {
// 支付结果通知 // 关键数据空值校验
if (StrUtil.isBlank(orderNo)) {
log.error("orderNo 为空");
notifyRecord.setHandleResult("orderNo 为空");
return Result.error("orderNo 为空");
}
if (StrUtil.isBlank(signData)) {
log.error("signData 为空: orderNo->{}", orderNo);
notifyRecord.setHandleResult("signData 为空");
return Result.error("signData 为空");
}
if (StrUtil.isBlank(returnActFlag)) {
log.error("returnActFlag 为空: orderNo->{}", orderNo);
notifyRecord.setHandleResult("returnActFlag 为空");
return Result.error("returnActFlag 为空");
}
chinaPayProperties.load();
if (StrUtil.isNotBlank(merchantNo) && !chinaPayProperties.getMerchantNo().equals(merchantNo)) {
log.error("商户号不匹配: 通知中的商户号->{}, 配置中的商户号->{}", merchantNo, chinaPayProperties.getMerchantNo());
notifyRecord.setHandleResult("商户号不匹配");
return Result.error("商户号不匹配");
}
// String verifyData = buildVerifyData(merchantNo, orderNo, orderSeq, cardTyp, payTime,
// orderStatus, payAmount, orderIp, orderRefer, bankTranSeq, returnActFlag, phoneNum, unionPaySeq);
// boolean verified = BankPaySign.getInstance().verifySignData(
// chinaPayProperties.getVerifyCerFile(),
// verifyData,
// signData);
// notifyRecord.setVerifyResult(verified ? 1 : 0);
//
// if (!verified) {
// log.error("签名验证失败: orderNo->{}", orderNo);
// notifyRecord.setHandleResult("签名验证失败");
// return Result.error("签名验证失败");
// }
log.info("签名验证通过: orderNo->{}", orderNo);
if (PayNotifyReturnActFlagEnum.PAY_RESULT_NOTIFY.getCode().equals(returnActFlag)) { if (PayNotifyReturnActFlagEnum.PAY_RESULT_NOTIFY.getCode().equals(returnActFlag)) {
// 设置30秒内同一个商户订单 不可重复接收到通知 Result r = handlePayResultNotify(orderNo, orderStatus, payAmount);
notifyRecord.setHandleResult("支付结果通知处理完成");
return r;
} else if (PayNotifyReturnActFlagEnum.PAY_PHONE_NOTIFY.getCode().equals(returnActFlag)) {
Result r = handlePayResultNotify(orderNo, orderStatus, payAmount);
notifyRecord.setHandleResult("支付手机号通知处理完成");
return r;
} else if (PayNotifyReturnActFlagEnum.PAY_REFUND_NOTIFY.getCode().equals(returnActFlag)) {
log.info("收到退款结果通知: orderNo->{}, orderStatus->{}", orderNo, orderStatus);
notifyRecord.setHandleResult("退款结果通知已接收");
return Result.success();
} else if (PayNotifyReturnActFlagEnum.ORDER_QUERY_NOTIFY.getCode().equals(returnActFlag)) {
log.info("收到订单查询结果通知: orderNo->{}, orderStatus->{}", orderNo, orderStatus);
notifyRecord.setHandleResult("订单查询结果通知已接收");
return Result.success();
} else {
log.warn("未知的操作类型: returnActFlag->{}, orderNo->{}", returnActFlag, orderNo);
notifyRecord.setHandleResult("未知操作类型: " + returnActFlag);
return Result.success();
}
} catch (Exception e) {
log.error("中国银行支付通知处理异常: orderNo->{}", orderNo, e);
notifyRecord.setHandleResult("处理异常: " + e.getMessage());
return Result.error();
} finally {
// try {
// chinaBankPayNotifyRecordService.update(notifyRecord);
// } catch (Exception ex) {
// log.error("更新通知记录失败: orderNo->{}", orderNo, ex);
// }
}
}
private Result handlePayResultNotify(String orderNo, String orderStatus, String payAmount) {
if (StrUtil.isBlank(orderNo)) {
log.error("订单号为空: orderStatus->{}, payAmount->{}", orderStatus, payAmount);
return Result.error("订单号为空");
}
if (chinaBankOrderPayNotifyCacheService.exists(orderNo)) { if (chinaBankOrderPayNotifyCacheService.exists(orderNo)) {
log.error("商户订单号重复支付结果通知:orderNo->{}", orderNo); log.warn("商户订单号重复支付结果通知: orderNo->{}", orderNo);
return Result.error("商户订单号重复通知"); return Result.error("商户订单号重复通知");
} }
chinaBankOrderPayNotifyCacheService.setex(orderNo, 30, orderNo); chinaBankOrderPayNotifyCacheService.setex(orderNo, 30, orderNo);
// 支付成功
if (PayNotifyOrderStatusEnum.PAY.getCode().equals(orderStatus)) { if (PayNotifyOrderStatusEnum.PAY.getCode().equals(orderStatus)) {
memberPayRecordService.paySuccess(orderNo, Double.valueOf(payAmount)); log.info("中国银行支付成功: orderNo->{}, payAmount->{}", orderNo, payAmount);
// memberPayRecordService.paySuccess(orderNo, Double.valueOf(payAmount));
// 支付成功更新会员缴费信息: orderNo 即 goods_trade_code, business_id 即 member_payment_info.id
MemberPayRecord payRecord = memberPayRecordService.getByGoodsTradeCode(orderNo);
if (payRecord != null && StrUtil.isNotBlank(payRecord.getBusinessId())) {
MemberPaymentInfo paymentInfo = memberPaymentService.fetch(payRecord.getBusinessId());
if (paymentInfo != null) {
paymentInfo.setPayment(true);
memberPaymentService.update(paymentInfo);
log.info("更新会员缴费信息成功: paymentInfoId->{}, orderNo->{}", payRecord.getBusinessId(), orderNo);
} else {
log.warn("未找到会员缴费信息: paymentInfoId->{}, orderNo->{}", payRecord.getBusinessId(), orderNo);
}
}
// 支付失败
} else if (PayNotifyOrderStatusEnum.FAIL.getCode().equals(orderStatus)) { } else if (PayNotifyOrderStatusEnum.FAIL.getCode().equals(orderStatus)) {
memberPayRecordService.payFail(orderNo, "用户支付失败"); log.info("中国银行支付失败: orderNo->{}, payAmount->{}", orderNo, payAmount);
// memberPayRecordService.payFail(orderNo, "用户支付失败");
} else {
log.info("中国银行支付未知状态: orderNo->{}, orderStatus->{}", orderNo, orderStatus);
} }
chinaBankOrderWaitPayCacheService.delItem(orderNo); chinaBankOrderWaitPayCacheService.delItem(orderNo);
}
}catch (Exception e){
e.printStackTrace();
return Result.error();
}
return Result.success(); return Result.success();
} }
private String buildVerifyData(String merchantNo, String orderNo, String orderSeq, String cardTyp,
String payTime, String orderStatus, String payAmount, String orderIp,
String orderRefer, String bankTranSeq, String returnActFlag,
String phoneNum, String unionPaySeq) {
StringBuilder sb = new StringBuilder();
sb.append(StrUtil.nullToEmpty(merchantNo)).append("|");
sb.append(StrUtil.nullToEmpty(orderNo)).append("|");
sb.append(StrUtil.nullToEmpty(orderSeq)).append("|");
sb.append(StrUtil.nullToEmpty(cardTyp)).append("|");
sb.append(StrUtil.nullToEmpty(payTime)).append("|");
sb.append(StrUtil.nullToEmpty(orderStatus)).append("|");
sb.append(StrUtil.nullToEmpty(payAmount)).append("|");
sb.append(StrUtil.nullToEmpty(orderIp)).append("|");
sb.append(StrUtil.nullToEmpty(orderRefer)).append("|");
sb.append(StrUtil.nullToEmpty(bankTranSeq)).append("|");
sb.append(StrUtil.nullToEmpty(returnActFlag)).append("|");
sb.append(StrUtil.nullToEmpty(phoneNum)).append("|");
sb.append(StrUtil.nullToEmpty(unionPaySeq)).append("|");
String verifyData = sb.toString();
log.info("验签数据: {}", verifyData);
return verifyData;
}
} }
@@ -1,13 +1,19 @@
package io.v.nutz.pay.record.controller; package io.v.nutz.pay.record.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.wizzer.framework.base.Result; import cn.wizzer.framework.base.Result;
import io.v.nutz.pay.annotations.Api; import io.v.nutz.pay.annotations.Api;
import io.v.nutz.pay.annotations.ApiOperation; import io.v.nutz.pay.annotations.ApiOperation;
import io.v.nutz.pay.bank.china.BankChinaWxPayService;
import io.v.nutz.pay.bank.china.pojo.RefundOrderResponse;
import io.v.nutz.pay.bank.china.pojo.WxPayOrderQueryResponse;
import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService; import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService;
import io.v.nutz.pay.common.CommonConstants;
import io.v.nutz.pay.enums.ChannelTypeEnum; import io.v.nutz.pay.enums.ChannelTypeEnum;
import io.v.nutz.pay.enums.MemberPayBusinessTypeEnum; import io.v.nutz.pay.enums.MemberPayBusinessTypeEnum;
import io.v.nutz.pay.enums.MemberPayStatusEnum;
import io.v.nutz.pay.enums.PayChannelEnum; import io.v.nutz.pay.enums.PayChannelEnum;
import io.v.nutz.pay.form.PayRecordForm; import io.v.nutz.pay.form.PayRecordForm;
import io.v.nutz.pay.pojo.LoginUser; import io.v.nutz.pay.pojo.LoginUser;
@@ -15,6 +21,8 @@ import io.v.nutz.pay.record.module.MemberPayRecord;
import io.v.nutz.pay.record.service.IMemberPayRecordService; import io.v.nutz.pay.record.service.IMemberPayRecordService;
import io.v.nutz.pay.util.AuthUtil; import io.v.nutz.pay.util.AuthUtil;
import io.v.nutz.pay.util.ServletUtil; import io.v.nutz.pay.util.ServletUtil;
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
import io.v.nutz.zhgh.memberCost.service.MemberPaymentService;
import org.nutz.integration.jedis.RedisService; import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
@@ -42,6 +50,12 @@ public class MemberPayRecordController {
@Inject @Inject
private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService; private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService;
@Inject
private BankChinaWxPayService bankChinaWxPayService;
@Inject
private MemberPaymentService memberPaymentService;
@Ok("json") @Ok("json")
@At("/get/by/code") @At("/get/by/code")
@@ -107,4 +121,122 @@ public class MemberPayRecordController {
chinaBankOrderWaitPayCacheService.delItem(payCode, 60 * 60 * 24); chinaBankOrderWaitPayCacheService.delItem(payCode, 60 * 60 * 24);
return Result.success(); return Result.success();
} }
@At("/refund")
@Ok("json")
@ApiOperation("支付记录退款(调用银行退款接口)")
public Result refund(String goodsTradeCode, Double refundAmount, HttpServletRequest request) {
Assert.isTrue(StrUtil.isNotBlank(goodsTradeCode), "缺少商品交易单号");
Assert.notNull(refundAmount, "缺少退款金额");
Assert.isTrue(refundAmount > 0, "退款金额必须大于0");
// 查询支付记录
MemberPayRecord record = memberPayRecordService.getByGoodsTradeCode(goodsTradeCode);
if (record == null) {
return Result.error("支付记录不存在");
}
if (!MemberPayStatusEnum.SUCCESS.getCode().equals(record.getStatus())) {
return Result.error("支付记录状态不支持退款");
}
if (record.getPayMoney() != null && refundAmount > record.getPayMoney()) {
return Result.error("退款金额不能大于支付金额");
}
LoginUser loginUser = AuthUtil.instance().getLoginUser();
if (loginUser == null || StrUtil.isBlank(loginUser.getId())) {
throw new RuntimeException("用户登录失效,请重新登录");
}
// 终端IP
String deviceIp = ServletUtil.getIp(request);
try {
RefundOrderResponse resp;
// 中国银行支付渠道
if (PayChannelEnum.CHINA_BANK.getCode().equals(record.getPayChannel())) {
resp = bankChinaWxPayService.refundOrder(goodsTradeCode, refundAmount, deviceIp);
} else {
return Result.error("不支持该支付渠道退款");
}
// 更新支付记录状态为已退款
record.setStatus(MemberPayStatusEnum.REFUND.getCode());
record.setLastRefundTime(DateUtil.date());
record.setRefundMoney(refundAmount);
record.setStatusReason("退款成功");
memberPayRecordService.update(record);
// 退款后重置会员缴费信息为未缴费: business_id 即 member_payment_info.id
if (MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType())
&& StrUtil.isNotBlank(record.getBusinessId())) {
MemberPaymentInfo paymentInfo = memberPaymentService.fetch(record.getBusinessId());
if (paymentInfo != null) {
paymentInfo.setPayment(false);
memberPaymentService.update(paymentInfo);
}
}
return Result.success(resp);
} catch (Exception e) {
e.printStackTrace();
return Result.error(e.getMessage());
}
}
@At("/query/order")
@Ok("json")
@ApiOperation("银行查询订单交易状态(PGWPortal/QueryOrderTrans.do)")
public Result queryOrderByBank(String goodsTradeCode) {
Assert.isTrue(StrUtil.isNotBlank(goodsTradeCode), "缺少商品交易单号");
// 查询本地支付记录,校验记录存在及渠道类型
MemberPayRecord record = memberPayRecordService.getByGoodsTradeCode(goodsTradeCode);
if (record == null) {
return Result.error("支付记录不存在");
}
if (!PayChannelEnum.CHINA_BANK.getCode().equals(record.getPayChannel())) {
return Result.error("不支持该支付渠道查询");
}
try {
// 调用银行查询接口
WxPayOrderQueryResponse response = bankChinaWxPayService.queryOrder(goodsTradeCode);
// 根据银行返回的交易状态同步更新本地支付记录
// 仅在本地记录仍为待支付状态时才同步,避免通知已处理后重复累加 payMoney
Integer currentStatus = record.getStatus();
boolean needSync = MemberPayStatusEnum.WAIT.getCode().equals(currentStatus);
String tranStatus = response.getTranStatus();
if (needSync && tranStatus != null
&& (tranStatus.equals(CommonConstants.ONE.toString()) || tranStatus.equals(CommonConstants.TWO.toString()))) {
if (tranStatus.equals(CommonConstants.ONE.toString())) {
// 支付成功
Double payAmount = response.getTranAmount() != null
? Double.valueOf(response.getTranAmount()) : record.getPayMoney();
memberPayRecordService.paySuccess(goodsTradeCode, payAmount);
// 同步更新会员缴费信息为已缴费
if (MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType())
&& StrUtil.isNotBlank(record.getBusinessId())) {
MemberPaymentInfo paymentInfo = memberPaymentService.fetch(record.getBusinessId());
if (paymentInfo != null) {
paymentInfo.setPayment(true);
memberPaymentService.update(paymentInfo);
}
}
} else {
// 支付失败
memberPayRecordService.payFail(goodsTradeCode, "银行查询结果:支付失败");
}
}
return Result.success(response);
} catch (Exception e) {
e.printStackTrace();
return Result.error(e.getMessage());
}
}
} }
@@ -113,4 +113,9 @@ public class MemberPayRecord implements Serializable {
@Column(value = "device_ip") @Column(value = "device_ip")
@ColDefine(type = ColType.VARCHAR, width = 100) @ColDefine(type = ColType.VARCHAR, width = 100)
private String deviceIp; private String deviceIp;
@Comment("设备IP")
@Column(value = "qr_code")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String qrCode;
} }
@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.v.nutz.base.service.impl.BaseServiceImpl; import io.v.nutz.base.service.impl.BaseServiceImpl;
import io.v.nutz.pay.bank.china.BankChinaWxPayService; import io.v.nutz.pay.bank.china.BankChinaWxPayService;
import io.v.nutz.pay.bank.china.pojo.WxAppPayResponse;
import io.v.nutz.pay.bank.china.pojo.WxPayOrderQueryResponse; import io.v.nutz.pay.bank.china.pojo.WxPayOrderQueryResponse;
import io.v.nutz.pay.bank.china.property.ChinaPayProperties; import io.v.nutz.pay.bank.china.property.ChinaPayProperties;
import io.v.nutz.pay.cache.ChinaBankOrderPayNotifyCacheService; import io.v.nutz.pay.cache.ChinaBankOrderPayNotifyCacheService;
@@ -20,7 +21,11 @@ import io.v.nutz.pay.order.service.IMemberPaymentOrderService;
import io.v.nutz.pay.record.module.MemberPayRecord; import io.v.nutz.pay.record.module.MemberPayRecord;
import io.v.nutz.pay.record.service.IMemberPayRecordService; import io.v.nutz.pay.record.service.IMemberPayRecordService;
import io.v.nutz.pay.util.BillNoUtil; import io.v.nutz.pay.util.BillNoUtil;
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
import io.v.nutz.zhgh.memberCost.service.MemberPaymentService;
import io.v.nutz.zhgh.memberCost.service.impl.MemberPaymentServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.dao.Sqls; import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql; import org.nutz.dao.sql.Sql;
@@ -31,7 +36,8 @@ import java.util.List;
@Slf4j @Slf4j
@IocBean(args = {"refer:dao"}) @IocBean(args = {"refer:dao"})
public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord> implements IMemberPayRecordService { public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord> implements IMemberPayRecordService
{
@Inject @Inject
private ChinaPayProperties chinaPayProperties; private ChinaPayProperties chinaPayProperties;
@@ -42,12 +48,16 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
@Inject @Inject
private BankChinaWxPayService bankChinaWxPayService; private BankChinaWxPayService bankChinaWxPayService;
@Inject
private MemberPaymentService memberPaymentService;
@Inject @Inject
private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService; private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService;
@Inject @Inject
private ChinaBankOrderPayNotifyCacheService chinaBankOrderPayNotifyCacheService; private ChinaBankOrderPayNotifyCacheService chinaBankOrderPayNotifyCacheService;
public MemberPayRecordServiceImpl(Dao dao) { public MemberPayRecordServiceImpl(Dao dao) {
super(dao); super(dao);
} }
@@ -57,9 +67,12 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
Sql sql = Sqls.create("select * from member_pay_record where pay_code = @pay_code limit 0,1"); Sql sql = Sqls.create("select * from member_pay_record where pay_code = @pay_code limit 0,1");
sql.setParam("pay_code", code); sql.setParam("pay_code", code);
List<MemberPayRecord> records = this.listEntity(sql); List<MemberPayRecord> records = this.listEntity(sql);
if(records != null && records.size() > 0) { if (records != null && records.size() > 0)
{
return records.get(0); return records.get(0);
} else { }
else
{
return null; return null;
} }
} }
@@ -69,9 +82,12 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
Sql sql = Sqls.create("select * from member_pay_record where id = @id limit 0,1"); Sql sql = Sqls.create("select * from member_pay_record where id = @id limit 0,1");
sql.params().set("id", id); sql.params().set("id", id);
List<MemberPayRecord> records = this.listEntity(sql); List<MemberPayRecord> records = this.listEntity(sql);
if(records != null && records.size() > 0) { if (records != null && records.size() > 0)
{
return records.get(0); return records.get(0);
} else { }
else
{
return null; return null;
} }
} }
@@ -81,9 +97,12 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
Sql sql = Sqls.create("select * from member_pay_record where goods_trade_code = @goods_trade_code limit 0,1"); Sql sql = Sqls.create("select * from member_pay_record where goods_trade_code = @goods_trade_code limit 0,1");
sql.params().set("goods_trade_code", goodsTradeCode); sql.params().set("goods_trade_code", goodsTradeCode);
List<MemberPayRecord> records = this.listEntity(sql); List<MemberPayRecord> records = this.listEntity(sql);
if(records != null && records.size() > 0) { if (records != null && records.size() > 0)
{
return records.get(0); return records.get(0);
} else { }
else
{
return null; return null;
} }
} }
@@ -92,7 +111,8 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
public List<MemberPayRecord> getByGoodsTradeCode(Object... goodsTradeOrders) { public List<MemberPayRecord> getByGoodsTradeCode(Object... goodsTradeOrders) {
Sql sql = Sqls.create("select * from member_pay_record where $goodsTradeOrders"); Sql sql = Sqls.create("select * from member_pay_record where $goodsTradeOrders");
StringBuilder where = new StringBuilder(); StringBuilder where = new StringBuilder();
for(int i = 0; i < goodsTradeOrders.length; i++) { for (int i = 0; i < goodsTradeOrders.length; i++)
{
where.append("goods_trade_code='" + goodsTradeOrders[i].toString() + "' or "); where.append("goods_trade_code='" + goodsTradeOrders[i].toString() + "' or ");
} }
String whereStr = where.toString().substring(0, where.toString().lastIndexOf("or")); String whereStr = where.toString().substring(0, where.toString().lastIndexOf("or"));
@@ -117,25 +137,34 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
record.setDeviceIp(deviceIp); record.setDeviceIp(deviceIp);
// 中国银行支付中商品交易单号需要特殊处理 该单号用于发起支付-> 商户订单号 // 中国银行支付中商品交易单号需要特殊处理 该单号用于发起支付-> 商户订单号
if(PayChannelEnum.CHINA_BANK.getCode().equals(payChannel)) { if (PayChannelEnum.CHINA_BANK.getCode().equals(payChannel))
{
record.setGoodsTradeCode(chinaPayProperties.getMerchantNoAfterChar(6).concat(BillNoUtil.getTimeNo(24))); record.setGoodsTradeCode(chinaPayProperties.getMerchantNoAfterChar(6).concat(BillNoUtil.getTimeNo(24)));
} else { }
else
{
record.setGoodsTradeCode(record.getPayCode()); record.setGoodsTradeCode(record.getPayCode());
} }
// 会员工会缴费 // 会员工会缴费
if(MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(businessType)) { if (MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(businessType))
// 会员工会缴费订单信息 {
MemberPaymentOrder paymentOrder = memberPaymentOrderService.getById(businessId);
if(paymentOrder == null) {
throw new RuntimeException("未获取到会员工会缴费订单信息");
}
record.setBusinessOrderCode(paymentOrder.getOrderCode());
record.setPayMoney(paymentOrder.getMoney());
record.setMemberId(paymentOrder.getMemberId());
record.setPayRemark("工会会员缴费-订单:" + paymentOrder.getOrderCode());
} else { List<MemberPaymentInfo> paymentUserList = memberPaymentService.query(Cnd.where("id", "=", businessId));
if (paymentUserList == null || paymentUserList.isEmpty())
{
throw new RuntimeException("缴费记录未找到");
}
MemberPaymentInfo memberPaymentInfo = paymentUserList.get(0);
record.setBusinessOrderCode(memberPaymentInfo.getTradeNo());
record.setPayMoney(Double.valueOf(memberPaymentInfo.getPaymentMoney()));
record.setMemberId(memberPaymentInfo.getUserId());
record.setPayRemark("工会会员缴费-订单:" + memberPaymentInfo.getLoginname());
}
else
{
throw new RuntimeException("不支持的支付业务"); throw new RuntimeException("不支持的支付业务");
} }
this.insert(record); this.insert(record);
@@ -145,7 +174,8 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
@Override @Override
public void paySuccess(String goodsTradeCode, Double oriPayAmount) { public void paySuccess(String goodsTradeCode, Double oriPayAmount) {
MemberPayRecord record = this.getByGoodsTradeCode(goodsTradeCode); MemberPayRecord record = this.getByGoodsTradeCode(goodsTradeCode);
if(record == null) { if (record == null)
{
throw new RuntimeException("商品交易单号不存在"); throw new RuntimeException("商品交易单号不存在");
} }
record.setStatus(MemberPayStatusEnum.SUCCESS.getCode()); record.setStatus(MemberPayStatusEnum.SUCCESS.getCode());
@@ -153,10 +183,13 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
this.updateById(record); this.updateById(record);
// 会员工会缴费 // 会员工会缴费
if(MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType())) { if (MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType()))
{
// 支付成功 // 支付成功
memberPaymentOrderService.paySuccess(record.getBusinessId(), oriPayAmount); memberPaymentOrderService.paySuccess(record.getBusinessId(), oriPayAmount);
} else { }
else
{
throw new RuntimeException("不支持的业务支付通知"); throw new RuntimeException("不支持的业务支付通知");
} }
} }
@@ -164,7 +197,8 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
@Override @Override
public void payFail(String goodsTradeCode, String failReason) { public void payFail(String goodsTradeCode, String failReason) {
MemberPayRecord record = this.getByGoodsTradeCode(goodsTradeCode); MemberPayRecord record = this.getByGoodsTradeCode(goodsTradeCode);
if(record == null) { if (record == null)
{
throw new RuntimeException("商品交易单号不存在"); throw new RuntimeException("商品交易单号不存在");
} }
record.setStatus(MemberPayStatusEnum.FAIL.getCode()); record.setStatus(MemberPayStatusEnum.FAIL.getCode());
@@ -173,73 +207,93 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
this.updateById(record); this.updateById(record);
// 会员工会缴费 // 会员工会缴费
if(MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType())) { if (MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType()))
{
// 支付成功 // 支付成功
memberPaymentOrderService.payFail(record.getBusinessId()); memberPaymentOrderService.payFail(record.getBusinessId());
} else { }
else
{
throw new RuntimeException("不支持的业务支付通知"); throw new RuntimeException("不支持的业务支付通知");
} }
} }
@Override @Override
public void checkChinaPayOrderStatus() { public void checkChinaPayOrderStatus() {
try { try
{
// 初始化配置参数 // 初始化配置参数
chinaPayProperties.load(); chinaPayProperties.load();
String goodsTradeOrder = chinaBankOrderWaitPayCacheService.get(); String goodsTradeOrder = chinaBankOrderWaitPayCacheService.get();
if (StrUtil.isBlank(goodsTradeOrder)) { if (StrUtil.isBlank(goodsTradeOrder))
{
log.info("未检测中国银行支付订单:null"); log.info("未检测中国银行支付订单:null");
return; return;
} }
List<String> goodsTradeOrders = JSON.parseArray(goodsTradeOrder, String.class); List<String> goodsTradeOrders = JSON.parseArray(goodsTradeOrder, String.class);
log.info("检测中国银行支付订单:{}", goodsTradeOrders.size()); log.info("检测中国银行支付订单:{}", goodsTradeOrders.size());
if (goodsTradeOrders.size() == 0) { if (goodsTradeOrders.size() == 0)
{
return; return;
} }
List<MemberPayRecord> payRecords = this.getByGoodsTradeCode(goodsTradeOrders.toArray()); List<MemberPayRecord> payRecords = this.getByGoodsTradeCode(goodsTradeOrders.toArray());
if (payRecords == null || payRecords.isEmpty()) { if (payRecords == null || payRecords.isEmpty())
{
return; return;
} }
for (MemberPayRecord record : payRecords) { for (MemberPayRecord record : payRecords)
if (!record.getStatus().equals(MemberPayStatusEnum.WAIT.getCode())) { {
if (!record.getStatus().equals(MemberPayStatusEnum.WAIT.getCode()))
{
continue; continue;
} }
if (!record.getPayChannel().equals(PayChannelEnum.CHINA_BANK.getCode())) { if (!record.getPayChannel().equals(PayChannelEnum.CHINA_BANK.getCode()))
{
continue; continue;
} }
WxPayOrderQueryResponse response = bankChinaWxPayService.queryOrder(record.getGoodsTradeCode()); WxPayOrderQueryResponse response = bankChinaWxPayService.queryOrder(record.getGoodsTradeCode());
if(chinaBankOrderPayNotifyCacheService.exists(record.getGoodsTradeCode())) { if (chinaBankOrderPayNotifyCacheService.exists(record.getGoodsTradeCode()))
{
continue; continue;
} }
chinaBankOrderPayNotifyCacheService.setex(record.getGoodsTradeCode(), 30, record.getGoodsTradeCode()); chinaBankOrderPayNotifyCacheService.setex(record.getGoodsTradeCode(), 30, record.getGoodsTradeCode());
if (response.getTranStatus() != null if (response.getTranStatus() != null
&&(response.getTranStatus().equals(CommonConstants.ONE.toString()) || response.getTranStatus().equals(CommonConstants.TWO.toString()))) { && (response.getTranStatus().equals(CommonConstants.ONE.toString()) || response.getTranStatus().equals(CommonConstants.TWO.toString())))
{
// 支付成功 // 支付成功
if(response.getTranStatus().equals(CommonConstants.ONE.toString())) { if (response.getTranStatus().equals(CommonConstants.ONE.toString()))
{
this.paySuccess(response.getOrderNo(), Double.valueOf(response.getTranAmount())); this.paySuccess(response.getOrderNo(), Double.valueOf(response.getTranAmount()));
// 支付失败 // 支付失败
} else { }
else
{
this.payFail(response.getOrderNo(), "支付失败"); this.payFail(response.getOrderNo(), "支付失败");
} }
chinaBankOrderWaitPayCacheService.delItem(record.getGoodsTradeCode()); chinaBankOrderWaitPayCacheService.delItem(record.getGoodsTradeCode());
} }
} }
} catch (Exception e) { }
catch (Exception e)
{
e.printStackTrace(); e.printStackTrace();
log.error("中国银行支付检测待支付订单异常:{}", e.getMessage()); log.error("中国银行支付检测待支付订单异常:{}", e.getMessage());
} }
} }
private MemberPayRecord sendPay(MemberPayRecord record) { private MemberPayRecord sendPay(MemberPayRecord record) {
try { try
Object requestResult = null; {
WxAppPayResponse requestResult = null;
// 中国银行支付渠道 // 中国银行支付渠道
if (PayChannelEnum.CHINA_BANK.getCode().equals(record.getPayChannel())) { if (PayChannelEnum.CHINA_BANK.getCode().equals(record.getPayChannel()))
{
// 微信APP支付 // 微信APP支付
if (ChannelTypeEnum.WX_APP.getCode().equals(record.getChannelType())) { if (ChannelTypeEnum.WX_APP.getCode().equals(record.getChannelType()))
{
// 获取中国银行微信支付拉起支付订单信息 // 获取中国银行微信支付拉起支付订单信息
requestResult = bankChinaWxPayService.appPay( requestResult = bankChinaWxPayService.appPay(
record.getGoodsTradeCode(), record.getGoodsTradeCode(),
@@ -251,14 +305,21 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
record.getDeviceIp()); record.getDeviceIp());
} }
} else { }
else
{
throw new RuntimeException("不支持的支付方式"); throw new RuntimeException("不支持的支付方式");
} }
if (requestResult != null) { if (requestResult != null)
{
record.setRequestResult(JSONObject.toJSONString(requestResult)); record.setRequestResult(JSONObject.toJSONString(requestResult));
record.setBusinessOrderCode(requestResult.getOrderSeq());
record.setQrCode(requestResult.getQrCode());
this.updateById(record); this.updateById(record);
} }
} catch (Exception e) { }
catch (Exception e)
{
this.payFail(record.getGoodsTradeCode(), e.getMessage()); this.payFail(record.getGoodsTradeCode(), e.getMessage());
throw new RuntimeException(e.getMessage()); throw new RuntimeException(e.getMessage());
} }
@@ -0,0 +1,13 @@
package io.v.nutz.pay.util;
public final class Base64 {
private static final Base64Coder coder = new Base64Coder();
public static String encode(byte abyte0[]) {
return coder.encode(abyte0);
}
public static byte[] decode(String s) {
return coder.decode(s);
}
}
@@ -0,0 +1,198 @@
package io.v.nutz.pay.util;
/**
*
* @description
*
* @author
*
* @modified_by
*/
public final class Base64Coder {
protected static boolean isWhiteSpace(char c) {
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
}
protected static boolean isPad(char c) {
return c == '=';
}
protected static boolean isData(char c) {
return c < '\200' && base64Alphabet[c] != -1;
}
protected static boolean isBase64(char c) {
return isWhiteSpace(c) || isPad(c) || isData(c);
}
public String encode(byte abyte0[]) {
if (abyte0 == null)
return null;
int i = abyte0.length * 8;
if (i == 0)
return "";
int j = i % 24;
int k = i / 24;
int l = j == 0 ? k : k + 1;
char ac[] = null;
ac = new char[l * 4];
int i1 = 0;
int j1 = 0;
for (int k1 = 0; k1 < k; k1++) {
byte byte5 = abyte0[j1++];
byte byte8 = abyte0[j1++];
byte byte10 = abyte0[j1++];
byte byte3 = (byte) (byte8 & 15);
byte byte0 = (byte) (byte5 & 3);
byte byte11 = (byte5 & -128) != 0 ? (byte) (byte5 >> 2 ^ 192) : (byte) (byte5 >> 2);
byte byte14 = (byte8 & -128) != 0 ? (byte) (byte8 >> 4 ^ 240) : (byte) (byte8 >> 4);
byte byte16 = (byte10 & -128) != 0 ? (byte) (byte10 >> 6 ^ 252) : (byte) (byte10 >> 6);
ac[i1++] = lookUpBase64Alphabet[byte11];
ac[i1++] = lookUpBase64Alphabet[byte14 | byte0 << 4];
ac[i1++] = lookUpBase64Alphabet[byte3 << 2 | byte16];
ac[i1++] = lookUpBase64Alphabet[byte10 & 63];
}
if (j == 8) {
byte byte6 = abyte0[j1];
byte byte1 = (byte) (byte6 & 3);
byte byte12 = (byte6 & -128) != 0 ? (byte) (byte6 >> 2 ^ 192) : (byte) (byte6 >> 2);
ac[i1++] = lookUpBase64Alphabet[byte12];
ac[i1++] = lookUpBase64Alphabet[byte1 << 4];
ac[i1++] = '=';
ac[i1++] = '=';
} else if (j == 16) {
byte byte7 = abyte0[j1];
byte byte9 = abyte0[j1 + 1];
byte byte4 = (byte) (byte9 & 15);
byte byte2 = (byte) (byte7 & 3);
byte byte13 = (byte7 & -128) != 0 ? (byte) (byte7 >> 2 ^ 192) : (byte) (byte7 >> 2);
byte byte15 = (byte9 & -128) != 0 ? (byte) (byte9 >> 4 ^ 240) : (byte) (byte9 >> 4);
ac[i1++] = lookUpBase64Alphabet[byte13];
ac[i1++] = lookUpBase64Alphabet[byte15 | byte2 << 4];
ac[i1++] = lookUpBase64Alphabet[byte4 << 2];
ac[i1++] = '=';
}
return new String(ac);
}
public byte[] decode(String s) {
if (s == null)
return null;
char ac[] = s.toCharArray();
int i = removeWhiteSpace(ac);
if (i % 4 != 0)
return null;
int j = i / 4;
if (j == 0)
return new byte[0];
byte abyte0[] = null;
byte byte0 = 0;
byte byte1 = 0;
char c = '\0';
char c1 = '\0';
char c2 = '\0';
char c3 = '\0';
int k = 0;
int l = 0;
int i1 = 0;
abyte0 = new byte[j * 3];
for (; k < j - 1; k++) {
if (!isData(c = ac[i1++]) || !isData(c1 = ac[i1++]) || !isData(c2 = ac[i1++]) || !isData(c3 = ac[i1++]))
return null;
byte0 = base64Alphabet[c];
byte1 = base64Alphabet[c1];
byte byte2 = base64Alphabet[c2];
byte byte5 = base64Alphabet[c3];
abyte0[l++] = (byte) (byte0 << 2 | byte1 >> 4);
abyte0[l++] = (byte) ((byte1 & 15) << 4 | byte2 >> 2 & 15);
abyte0[l++] = (byte) (byte2 << 6 | byte5);
}
if (!isData(c = ac[i1++]) || !isData(c1 = ac[i1++]))
return null;
byte0 = base64Alphabet[c];
byte1 = base64Alphabet[c1];
c2 = ac[i1++];
c3 = ac[i1++];
if (!isData(c2) || !isData(c3)) {
if (isPad(c2) && isPad(c3)) {
if ((byte1 & 15) != 0)
return null;
byte abyte1[] = new byte[k * 3 + 1];
System.arraycopy(abyte0, 0, abyte1, 0, k * 3);
abyte1[l] = (byte) (byte0 << 2 | byte1 >> 4);
return abyte1;
}
if (isPad(c2) && isPad(c3))
return null;
byte byte3 = base64Alphabet[c2];
if ((byte3 & 3) != 0)
return null;
byte abyte2[] = new byte[k * 3 + 2];
System.arraycopy(abyte0, 0, abyte2, 0, k * 3);
abyte2[l++] = (byte) (byte0 << 2 | byte1 >> 4);
abyte2[l] = (byte) ((byte1 & 15) << 4 | byte3 >> 2 & 15);
return abyte2;
}
byte byte4 = base64Alphabet[c2];
byte byte6 = base64Alphabet[c3];
abyte0[l++] = (byte) (byte0 << 2 | byte1 >> 4);
abyte0[l++] = (byte) ((byte1 & 15) << 4 | byte4 >> 2 & 15);
abyte0[l++] = (byte) (byte4 << 6 | byte6);
return abyte0;
}
protected static int removeWhiteSpace(char ac[]) {
if (ac == null)
return 0;
int i = 0;
int j = ac.length;
for (int k = 0; k < j; k++)
if (!isWhiteSpace(ac[k]))
ac[i++] = ac[k];
return i;
}
private static final byte base64Alphabet[];
private static final char lookUpBase64Alphabet[];
static {
base64Alphabet = new byte[128];
lookUpBase64Alphabet = new char[64];
for (int i = 0; i < 128; i++)
base64Alphabet[i] = -1;
for (int j = 90; j >= 65; j--)
base64Alphabet[j] = (byte) (j - 65);
for (int k = 122; k >= 97; k--)
base64Alphabet[k] = (byte) ((k - 97) + 26);
for (int l = 57; l >= 48; l--)
base64Alphabet[l] = (byte) ((l - 48) + 52);
base64Alphabet[43] = 62;
base64Alphabet[47] = 63;
for (int i1 = 0; i1 <= 25; i1++)
lookUpBase64Alphabet[i1] = (char) (65 + i1);
int j1 = 26;
for (int k1 = 0; j1 <= 51; k1++) {
lookUpBase64Alphabet[j1] = (char) (97 + k1);
j1++;
}
int l1 = 52;
for (int i2 = 0; l1 <= 61; i2++) {
lookUpBase64Alphabet[l1] = (char) (48 + i2);
l1++;
}
lookUpBase64Alphabet[62] = '+';
lookUpBase64Alphabet[63] = '/';
}
}
+86 -6
View File
@@ -1,8 +1,11 @@
package io.v.nutz.pay.util; package io.v.nutz.pay.util;
import java.io.*; import org.w3c.dom.Document;
import java.util.ArrayList; import org.w3c.dom.Element;
import java.util.List; import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer; import javax.xml.transform.Transformer;
@@ -11,9 +14,15 @@ import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource; import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*; import javax.xml.validation.Schema;
import org.w3c.dom.*; import javax.xml.validation.SchemaFactory;
import org.xml.sax.InputSource; import javax.xml.validation.Validator;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
public class XmlUtil public class XmlUtil
{ {
@@ -64,6 +73,9 @@ public class XmlUtil
public static Document createDocument(String s) public static Document createDocument(String s)
throws Exception throws Exception
{ {
// 预处理XML,修复未转义的&符号
s = fixUnescapedAmpersands(s);
DocumentBuilder documentbuilder = createDocumentBuilder(); DocumentBuilder documentbuilder = createDocumentBuilder();
return documentbuilder.parse(new ByteArrayInputStream(s.getBytes("UTF-8"))); return documentbuilder.parse(new ByteArrayInputStream(s.getBytes("UTF-8")));
} }
@@ -71,6 +83,9 @@ public class XmlUtil
public static Document createDocument(String s, String s1) public static Document createDocument(String s, String s1)
throws Exception throws Exception
{ {
// 预处理XML,修复未转义的&符号
s = fixUnescapedAmpersands(s);
DocumentBuilder documentbuilder = createDocumentBuilder(); DocumentBuilder documentbuilder = createDocumentBuilder();
return documentbuilder.parse(new ByteArrayInputStream(s.getBytes(s1))); return documentbuilder.parse(new ByteArrayInputStream(s.getBytes(s1)));
} }
@@ -203,4 +218,69 @@ public class XmlUtil
transformer.transform(domsource, new StreamResult(stringwriter)); transformer.transform(domsource, new StreamResult(stringwriter));
return stringwriter.toString(); return stringwriter.toString();
} }
private static String fixUnescapedAmpersands(String xml) {
if (xml == null || xml.isEmpty()) {
return xml;
}
StringBuilder result = new StringBuilder();
int i = 0;
int len = xml.length();
while (i < len) {
char c = xml.charAt(i);
// 如果找到&符号
if (c == '&') {
// 检查是否是已经正确转义的实体(如 &amp; &lt; &#等)
if (i + 1 < len) {
int j = i + 1;
// 查找分号
boolean foundSemicolon = false;
boolean isValidEntity = false;
while (j < len && j < i + 10) { // 实体引用通常不会太长
if (xml.charAt(j) == ';') {
foundSemicolon = true;
// 检查&和;之间的内容是否是合法的实体名或数值引用
String entityContent = xml.substring(i + 1, j);
if (entityContent.startsWith("#") ||
entityContent.equals("amp") ||
entityContent.equals("lt") ||
entityContent.equals("gt") ||
entityContent.equals("quot") ||
entityContent.equals("apos")) {
isValidEntity = true;
}
break;
} else if (xml.charAt(j) == '&' || xml.charAt(j) == '<' || xml.charAt(j) == '>') {
// 遇到其他特殊字符,说明这不是一个有效的实体引用
break;
}
j++;
}
// 如果是合法的实体引用,保持原样
if (foundSemicolon && isValidEntity) {
result.append(c);
i++;
} else {
// 否则将&替换为&amp;
result.append("&amp;");
i++;
}
} else {
// &是最后一个字符,替换为&amp;
result.append("&amp;");
i++;
}
} else {
result.append(c);
i++;
}
}
return result.toString();
}
} }
@@ -11,6 +11,7 @@ import io.v.nutz.sys.services.SysMenuService;
import io.v.nutz.sys.services.SysUserService; import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.web.commons.base.Globals; import io.v.nutz.web.commons.base.Globals;
import io.v.nutz.web.commons.utils.ShiroUtil; import io.v.nutz.web.commons.utils.ShiroUtil;
import io.v.nutz.zhgh.activity.models.ActivitySchool;
import io.v.nutz.zhgh.activity.models.ActivityUserScope; import io.v.nutz.zhgh.activity.models.ActivityUserScope;
import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
@@ -211,6 +212,35 @@ public class SysHomeController {
public Result listHomeActivity() { public Result listHomeActivity() {
FieldFilter fieldFilter = FieldFilter.locked(Sys_home_activity.class, "classPath"); FieldFilter fieldFilter = FieldFilter.locked(Sys_home_activity.class, "classPath");
List<Sys_home_activity> list = Daos.ext(dao,fieldFilter) .query(Sys_home_activity.class, Cnd.where("enable", "=", 1).desc("top")); List<Sys_home_activity> list = Daos.ext(dao,fieldFilter) .query(Sys_home_activity.class, Cnd.where("enable", "=", 1).desc("top"));
boolean sportsApplyRole = ShiroUtil.hasAnyRoles("H04,sysadmin,SCHOOL_UNION_SPORTS_ADMIN,gh08,gh01");
// 体育活动未写入首页活动表,在此转换以兼容已有数据
List<ActivitySchool> sportsActivities = dao.query(ActivitySchool.class, Cnd.where("isDisabled", "=", true)
.and("close", "!=", true)
.and("isSave", "!=", true)
.desc("applyStartTime"));
for (ActivitySchool sportsActivity : sportsActivities) {
if (StrUtil.isBlank(sportsActivity.getApplyStartTime()) || StrUtil.isBlank(sportsActivity.getApplyEndTime())) {
continue;
}
// 首页为PC入口,普通会员没有体育活动个人报名页面
if (!sportsApplyRole) {
continue;
}
Sys_home_activity homeActivity = new Sys_home_activity();
homeActivity.setId(sportsActivity.getId());
homeActivity.setName(sportsActivity.getName());
if (Lang.isNotEmpty(sportsActivity.getImage())) {
homeActivity.setCover(sportsActivity.getImage().get(0).getId());
}
homeActivity.setUrl("/platform/activity/apply");
homeActivity.setH5Url("/mobile/activity/Sports/list/activityList?id=" + sportsActivity.getId() + "&signUpMethod=2");
homeActivity.setAllowUserGroupId(sportsActivity.getActivityGroupId());
homeActivity.setStartDate(DateUtil.parse(sportsActivity.getApplyStartTime()));
homeActivity.setEndDate(DateUtil.parse(sportsActivity.getApplyEndTime()));
homeActivity.setEnable(true);
homeActivity.setType("Activity");
list.add(homeActivity);
}
String userId = ShiroUtil.getUserId(); String userId = ShiroUtil.getUserId();
List<Sys_home_activity> allowActivityList = new ArrayList<>(); List<Sys_home_activity> allowActivityList = new ArrayList<>();
// 今天的时间 // 今天的时间
@@ -226,6 +256,11 @@ public class SysHomeController {
Integer allowUserGroupId = activity.getAllowUserGroupId(); Integer allowUserGroupId = activity.getAllowUserGroupId();
String allowUserSql = activity.getAllowUserSql(); String allowUserSql = activity.getAllowUserSql();
activity.setAllowUserSql(null); activity.setAllowUserSql(null);
// 体育活动管理员与报名页保持一致,不受活动分组限制
if ("Activity".equals(activity.getType()) && sportsApplyRole) {
allowActivityList.add(activity);
continue;
}
if (allowUserGroupId == null && StrUtil.isBlank(allowUserSql)) { if (allowUserGroupId == null && StrUtil.isBlank(allowUserSql)) {
allowActivityList.add(activity); allowActivityList.add(activity);
continue; continue;
@@ -80,4 +80,9 @@ public class Sys_home_activity extends BaseModel {
@ColDefine(type = ColType.DATETIME) @ColDefine(type = ColType.DATETIME)
private Date endDate; private Date endDate;
/**
* 业务类型,不保存到数据库
*/
private String type;
} }
@@ -63,7 +63,7 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
* @param user * @param user
* @return * @return
*/ */
@CacheResult(cacheKey = "${args[0].id}_getRoleCodeList") // @CacheResult(cacheKey = "${args[0].id}_getRoleCodeList")
public List<String> getRoleCodeList(Sys_user user) { public List<String> getRoleCodeList(Sys_user user) {
dao().fetchLinks(user, "roles"); dao().fetchLinks(user, "roles");
List<String> roleNameList = new ArrayList<String>(); List<String> roleNameList = new ArrayList<String>();
@@ -76,7 +76,7 @@ public class NutShiroProcessor extends AbstractProcessor {
"/platform/qywechat/.*", "/platform/qywechat/.*",
"/platform/wechat/.*", "/platform/wechat/.*",
"/platform/home/(500|403|404|UnknownAccountError|LockedAccountError)", "/platform/home/(500|403|404|UnknownAccountError|LockedAccountError)",
"/mobile/login", // "/mobile/login",
"/mobile/login/doLogin", "/mobile/login/doLogin",
"/platform/jsz/login", "/platform/jsz/login",
"/platform/activity/basic/scope/getScopeUser", "/platform/activity/basic/scope/getScopeUser",
@@ -308,7 +308,7 @@ public class ActivityApplyController {
cndTeam.and("activityId", "=", activityId); cndTeam.and("activityId", "=", activityId);
List<ActivitySchoolTeam> teamList = activitySchoolTeamService.dao().query(ActivitySchoolTeam.class, cndTeam.orderBy("CAST(SUBSTRING(name FROM 2 FOR LOCATE('队', name) - 2) AS UNSIGNED)", "ASC")); List<ActivitySchoolTeam> teamList = activitySchoolTeamService.dao().query(ActivitySchoolTeam.class, cndTeam.orderBy("CAST(SUBSTRING(name FROM 2 FOR LOCATE('队', name) - 2) AS UNSIGNED)", "ASC"));
if (activity.getApplyMultipleTeamsUnionIds().contains(Vi.getUnionId())) { if (Lang.isNotEmpty(activity.getApplyMultipleTeamsUnionIds()) && activity.getApplyMultipleTeamsUnionIds().contains(Vi.getUnionId())) {
result.put("team", teamList); result.put("team", teamList);
} else { } else {
result.put("team", Lang.isNotEmpty(teamList) ? List.of(teamList.get(0)) : teamList); result.put("team", Lang.isNotEmpty(teamList) ? List.of(teamList.get(0)) : teamList);
@@ -100,7 +100,7 @@ public class LxyCommon {
public Object getNowHdList(@Param(value = "year", required = false) Integer year) { public Object getNowHdList(@Param(value = "year", required = false) Integer year) {
Sql sql = Sqls.create("SELECT id,`name` FROM `lxy_hd` $condition"); Sql sql = Sqls.create("SELECT id,`name` FROM `lxy_hd` $condition");
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.and(new org.nutz.dao.util.cri.Static("now() < bmjzsj")); cnd.and(new org.nutz.dao.util.cri.Static("now() >= bmkssj AND now() < bmjzsj"));
if (year != null) { if (year != null) {
cnd.and(new org.nutz.dao.util.cri.Static("YEAR(hdcjsj) = " + year)); cnd.and(new org.nutz.dao.util.cri.Static("YEAR(hdcjsj) = " + year));
} }
@@ -120,7 +120,7 @@ public class LxyGhbmController {
"""); """);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.and(new Static("now() < hd.bmjzsj")); cnd.and(new Static("now() >= hd.bmkssj AND now() < hd.bmjzsj"));
cnd.and("mefp.hdid", "=", hdid); cnd.and("mefp.hdid", "=", hdid);
cnd.andEX("un.id", "=", unionId); cnd.andEX("un.id", "=", unionId);
@@ -243,6 +243,10 @@ public class LxyGhbmController {
return Result.error("未获取到报名数据,请选择人员报名"); return Result.error("未获取到报名数据,请选择人员报名");
} }
if (!checkActivitySignUpTime(bmList.get(0).getHdid())) {
return Result.error("当前不在报名时间范围内");
}
if (!checkActivityUserScope(bmList.get(0).getHdid(), bmList.stream().map(Lxy_bm::getUserid).collect(Collectors.toList()))) { if (!checkActivityUserScope(bmList.get(0).getHdid(), bmList.stream().map(Lxy_bm::getUserid).collect(Collectors.toList()))) {
return Result.error("存在不在活动人员范围内的人员,请重新选择"); return Result.error("存在不在活动人员范围内的人员,请重新选择");
} }
@@ -389,6 +393,9 @@ public class LxyGhbmController {
try { try {
lock.lock(); lock.lock();
String unionId = Vi.getUnionId(); String unionId = Vi.getUnionId();
if (!checkActivitySignUpTime(projectId)) {
return Result.error("当前不在报名时间范围内");
}
// 已经报名的,排除审核未通过 // 已经报名的,排除审核未通过
int count = dao.count(Lxy_bm.class, Cnd.where("hdid", "=", projectId).and("ghid", "=", unionId) int count = dao.count(Lxy_bm.class, Cnd.where("hdid", "=", projectId).and("ghid", "=", unionId)
.and(Cnd.exps("bmzt", "in", List.of("1", "3", "5", "6")).or("bmzt", "is", null))); .and(Cnd.exps("bmzt", "in", List.of("1", "3", "5", "6")).or("bmzt", "is", null)));
@@ -538,6 +545,11 @@ public class LxyGhbmController {
return dao.count("activity_user_scope", Cnd.where("groupId", "=", activityGroupId).and("userId", "in", ids)) == ids.size(); return dao.count("activity_user_scope", Cnd.where("groupId", "=", activityGroupId).and("userId", "in", ids)) == ids.size();
} }
private boolean checkActivitySignUpTime(String hdid) {
return lxyHdService.count(Cnd.where("id", "=", hdid)
.and(new Static("now() >= bmkssj AND now() < bmjzsj"))) > 0;
}
private Integer getActivityGroupId(String hdid) { private Integer getActivityGroupId(String hdid) {
Lxy_hd hd = lxyHdService.fetch(hdid); Lxy_hd hd = lxyHdService.fetch(hdid);
return hd == null ? null : hd.getActivityGroupId(); return hd == null ? null : hd.getActivityGroupId();
@@ -79,7 +79,7 @@ public class MHealthCheckupListController {
FROM FROM
health_checkup_project p health_checkup_project p
LEFT JOIN health_checkup_user hcu ON hcu.projectId = p.id LEFT JOIN health_checkup_user hcu ON hcu.projectId = p.id
LEFT JOIN health_checkup_user_selection hs ON hs.selectUserId = hcu.userId LEFT JOIN health_checkup_user_selection hs ON hs.selectUserId = hcu.userId and hs.projectId=p.id
$condition $condition
"""); """);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
@@ -220,6 +220,23 @@ public class WelfareListController {
return null; return null;
} }
/**
* 更新福利名单中的单位信息。
*
* @param projectId 福利项目ID,前端必须传入当前选中的福利项目
* @return Integer,表示实际更新的福利名单数量
*/
@At
@ViReturn
@Aop(TransAop.READ_COMMITTED)
@RequiresPermissions("welfare.list.mange")
public Object updateWelfareListUnitInfo(@Param("projectId") String projectId) {
if (StrUtil.isBlank(projectId)) {
return Result.error("请选择福利项目");
}
return welfareListService.updateWelfareListUnitInfo(projectId);
}
@At @At
@ViReturn @ViReturn
@@ -29,5 +29,13 @@ public interface WelfareListService extends ViService<WelfareList> {
*/ */
void joinWelfareList(String projectId, List<String> userIds); void joinWelfareList(String projectId, List<String> userIds);
/**
* 将指定福利项目名单中的福利单位和二级单位,同步为用户主档案中的最新信息。
*
* @param projectId 福利项目ID
* @return 实际更新的福利名单数量
*/
int updateWelfareListUnitInfo(String projectId);
List<NutMap> getToNotWelfareUsers(String projectId); List<NutMap> getToNotWelfareUsers(String projectId);
} }
@@ -1,9 +1,11 @@
package io.v.nutz.zhgh.welfare.service.impl; package io.v.nutz.zhgh.welfare.service.impl;
import cn.hutool.core.util.StrUtil;
import io.v.nutz.base.dao.CndPlus; import io.v.nutz.base.dao.CndPlus;
import io.v.nutz.base.service.impl.ViServiceImpl; import io.v.nutz.base.service.impl.ViServiceImpl;
import io.v.nutz.base.utils.DateUtil; import io.v.nutz.base.utils.DateUtil;
import io.v.nutz.sys.models.Sys_user; import io.v.nutz.sys.models.Sys_user;
import io.v.nutz.sys.models.User;
import io.v.nutz.zhgh.welfare.mode.WelfareUnitMode; import io.v.nutz.zhgh.welfare.mode.WelfareUnitMode;
import io.v.nutz.zhgh.welfare.model.WelfareList; import io.v.nutz.zhgh.welfare.model.WelfareList;
import io.v.nutz.zhgh.welfare.model.WelfareProject; import io.v.nutz.zhgh.welfare.model.WelfareProject;
@@ -24,6 +26,7 @@ import org.nutz.plugins.wkcache.annotation.CacheDefaults;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -120,6 +123,45 @@ public class WelfareListServiceImpl extends ViServiceImpl<WelfareList> implement
// joinWelfareList(listMap(sql), projectId); // joinWelfareList(listMap(sql), projectId);
} }
/**
* 福利名单保存的是生成名单时的单位快照;人员调动后,通过用户ID关联 user 视图更新该快照。
* 视图中的工会或单位为空时不参与同步,避免将名单中已有的有效信息清空。
*
* @param projectId 福利项目ID
* @return 实际更新的福利名单数量
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public int updateWelfareListUnitInfo(String projectId) {
List<WelfareList> welfareLists = query(Cnd.where("projectId", "=", projectId));
if (welfareLists.isEmpty()) {
return 0;
}
List<String> userIds = welfareLists.stream()
.map(WelfareList::getUserId)
.collect(Collectors.toList());
List<User> users = dao().query(User.class, Cnd.where("id", "in", userIds));
Map<String, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, user -> user));
// 只同步视图中单位和工会信息完整的人员,避免用空值覆盖福利名单原有快照。
List<WelfareList> updateLists = new ArrayList<>();
for (WelfareList welfareList : welfareLists) {
User user = userMap.get(welfareList.getUserId());
if (user == null || StrUtil.isBlank(user.getUnionid()) || StrUtil.isBlank(user.getUnitid())) {
continue;
}
welfareList.setWelfareUnitId(user.getUnionid());
welfareList.setWelfareSecondLevelUnitId(user.getUnitid());
updateLists.add(welfareList);
}
if (updateLists.isEmpty()) {
return 0;
}
return dao().update(updateLists, "^(welfareUnitId|welfareSecondLevelUnitId)$");
}
@Override @Override
public List<NutMap> getToNotWelfareUsers(String projectId) { public List<NutMap> getToNotWelfareUsers(String projectId) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
+27 -2
View File
@@ -34,7 +34,7 @@ redis.pool.minIdle=10
#redis.mode=cluster #redis.mode=cluster
redis.mode=normal redis.mode=normal
#redis.nodes=192.168.6.31:6377,192.168.6.31:6378,192.168.6.28:6377,192.168.6.28:6378,192.168.6.34:6377,192.168.6.34:6378 #redis.nodes=192.168.6.31:6377,192.168.6.31:6378,192.168.6.28:6377,192.168.6.28:6378,192.168.6.34:6377,192.168.6.34:6378
jdbc.url=jdbc:mysql://10.10.10.16:3306/zhgh_hrbnu?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true jdbc.url=jdbc:mysql://10.10.10.16:3306/zhgh_hrbnu?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&sessionVariables=sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'
#jdbc.driver=dm.jdbc.driver.DmDriver #jdbc.driver=dm.jdbc.driver.DmDriver
#jdbc.url=jdbc:dm://127.0.0.1:5236?databaseProductName=dm mysql&compatibleMode=mysql #jdbc.url=jdbc:dm://127.0.0.1:5236?databaseProductName=dm mysql&compatibleMode=mysql
jdbc.username=root jdbc.username=root
@@ -141,4 +141,29 @@ logback.exts.loglevel.enabled=true
logback.exts.loglevel.heartbeat=15 logback.exts.loglevel.heartbeat=15
########################中国银行支付#################
# 签名证书 文件地址
bank.china.sign.key.file=D:\\zgyh\\95566SZ010006435.pfx
#bank.china.sign.key.file=/opt/server/wmkj/hsdzgyh.pfx
# 支付网关PGWPortal地址
bank.china.pgw.portal.url=
# 上传下载BocnetExpress地址
bank.china.bocnet.express.url=
# 签名证书密码
bank.china.sign.key.password=111111
# 验签的中行根证书
bank.china.verify.cer.file=D:\\zgyh\\boccfcaTest.cer
#bank.china.verify.cer.file=/opt/server/wmkj/sub.cer
# 下载文件保存路径
bank.china.download.dir=
# 上传的文件路径
bank.china.upload.file=
# API请求域名
bank.china.domain=http://219.145.85.186
# 商户号
bank.china.merchant.no=104670110688695
# 子商户公众账号ID
bank.china.sub.appid=wx0ad18b7f3c9ad6ab
# 支付结果通知域名
#bank.china.notify.domain=https://zhgh.hrbnu.edu.cn
bank.china.notify.domain=https://202.118.128.22
@@ -157,16 +157,16 @@ layout("/mobile/platform.html"){
</template> </template>
<van-radio-group checked-color="#1867b0" v-model="moneyModel"> <van-radio-group checked-color="#1867b0" v-model="moneyModel">
<van-cell @click="moneyModel = 1" clickable title="支付宝"> <!-- <van-cell @click="moneyModel = 1" clickable title="支付宝">-->
<template #icon> <!-- <template #icon>-->
<div class="pay_img" style="margin-right: 4px"> <!-- <div class="pay_img" style="margin-right: 4px">-->
<img src="/assets/mobile/svg/memberPay/zfb.svg"/> <!-- <img src="/assets/mobile/svg/memberPay/zfb.svg"/>-->
</div> <!-- </div>-->
</template> <!-- </template>-->
<template #right-icon> <!-- <template #right-icon>-->
<van-radio :name="1"></van-radio> <!-- <van-radio :name="1"></van-radio>-->
</template> <!-- </template>-->
</van-cell> <!-- </van-cell>-->
<van-cell @click="moneyModel = 2" clickable title="微信"> <van-cell @click="moneyModel = 2" clickable title="微信">
<template #icon> <template #icon>
<div class="pay_img" style="margin-right: 4px"> <div class="pay_img" style="margin-right: 4px">
@@ -405,51 +405,17 @@ layout("/mobile/platform.html"){
return; return;
} }
// 微信支付 初始化微信JS-SDK配置 const requestResult = JSON.parse(data.requestResult)
if(paymentMethod.type === 'wx') {
await this.loadWxJsConfig();
wx.ready(function () { if(requestResult.qrCode) {
this.payToOtherPage = true
// 中国银行 微信APP支付
if(data.payChannel === 1 && data.channelType === 1) {
const payParams = JSON.parse(data.requestResult)
console.log("服务端获取的支付参数payParams->", payParams)
const prepayData = {
appId: payParams.wcPayDataAppId, // 自己的APPID
timeStamp: payParams.wcPayDataTimeStamp, // 时间戳
nonceStr: payParams.wcPayDataNonceStr, // 随机字符串
package: payParams.wcPayDataPrepayId, // 预支付订单号
signType: payParams.wcPayDataSignType, // 签名类型
paySign: payParams.wcPayDataPaySign // 签名
};
console.log("调用微信API支付参数->", prepayData)
// 调用微信支付API
wx.chooseWXPay({
timestamp: prepayData.timeStamp,
nonceStr: prepayData.nonceStr,
package: prepayData.package,
signType: prepayData.signType,
paySign: prepayData.paySign,
success: function(res) {
// 支付成功后的操作
console.log('支付成功');
setTimeout(() => { setTimeout(() => {
this.paySuccess(res, data.goodsTradeCode) window.location.href = requestResult.qrCode
}, 500); }, 500)
},
cancel: function(res) { this.startPaymentPolling(data.goodsTradeCode)
// 用户取消支付的操作 } else {
console.log('支付取消'); vant.Toast.fail("订单支付链接不存在")
},
fail: function(res) {
// 支付失败的操作
console.log('支付失败');
this.payFail(res, data.goodsTradeCode)
}
});
}
})
} }
} }
}).catch((e) => { }).catch((e) => {
@@ -505,6 +471,41 @@ layout("/mobile/platform.html"){
async payFail(res, goodsTradeCode) { async payFail(res, goodsTradeCode) {
await $.post('/member/pay/record/del/check/pay/code?payCode=' + goodsTradeCode) await $.post('/member/pay/record/del/check/pay/code?payCode=' + goodsTradeCode)
}, },
async startPaymentPolling(goodsTradeCode) {
let pollCount = 0
const maxPollCount = 60
const pollInterval = setInterval(async () => {
pollCount++
try {
const res = await $.get('/member/pay/record/get/by/code?goodsTradeCode=' + goodsTradeCode)
if(res.code === 0 && res.data) {
if(res.data.status === 1) {
clearInterval(pollInterval)
this.$toast.success('支付成功')
this.payToOtherPage = false
this.payVisible = false
this.doSearch()
return
} else if(res.data.status === 2) {
clearInterval(pollInterval)
this.$toast.fail('支付失败')
this.payToOtherPage = false
return
}
}
if(pollCount >= maxPollCount) {
clearInterval(pollInterval)
this.$toast('支付结果查询超时,请稍后刷新查看')
}
} catch(e) {
console.error('轮询支付状态失败', e)
}
}, 2000)
},
}, },
async created() { async created() {
let nowYear = new Date().getFullYear() let nowYear = new Date().getFullYear()
@@ -331,18 +331,18 @@ layout("/layouts/platform.html"){
</template> </template>
<template #func> <template #func>
<!-- <div class="mr10" v-if="unionLeaderCoach">--> <div class="mr10" v-if="unionLeaderCoach">
<!-- &lt;!&ndash;团长:<span style="color: #419BF8">{{headData.username | str}}</span>&emsp13;&ndash;&gt;&ndash;&gt;--> <!--团长:<span style="color: #419BF8">{{headData.username | str}}</span>&emsp13;&ndash;&gt;-->
<!-- 领队兼教练:<span style="color: #419BF8">{{LeaderData.username | str}} </span>&emsp13;--> 领队兼教练:<span style="color: #419BF8">{{LeaderData.username | str}} </span>&emsp13;
<!--&lt;!&ndash; 教练:<span style="color: #419BF8">{{CoachData.username | str}}</span>&ndash;&gt;--> <!-- 教练:<span style="color: #419BF8">{{CoachData.username | str}}</span>-->
<!-- &lt;!&ndash; 工作人员:<span--> <!-- 工作人员:<span
<!-- style="color: #419BF8">{{staffData.username | str}}</span>&ndash;&gt;--> style="color: #419BF8">{{staffData.username | str}}</span>-->
<!-- </div>--> </div>
<!-- <div>--> <div>
<!-- <el-button @click="openLeaderCoach" type="primary"--> <el-button @click="openLeaderCoach" type="primary"
<!-- v-if="unionLeaderCoach">设置领队/教练--> v-if="unionLeaderCoach">设置领队/教练
<!-- </el-button>--> </el-button>
<!-- </div>--> </div>
</template> </template>
</table-tool> </table-tool>
@@ -942,7 +942,7 @@ layout("/layouts/platform.html"){
title="添加领队/教练" title="添加领队/教练"
width="60%" width="60%"
> >
<el-form :model="headData" :rules="formRules" label-width="100px" ref="form"> <el-form :model="LeaderData" :rules="formRules" label-width="100px" ref="leaderCoachForm">
<el-row gutter="20"> <el-row gutter="20">
<!-- <el-col :span="12"> <!-- <el-col :span="12">
<vi-title title="填写团长信息"></vi-title> <vi-title title="填写团长信息"></vi-title>
@@ -1775,7 +1775,7 @@ layout("/layouts/platform.html"){
return return
}*/ }*/
this.$refs["form"].validate(async (valid) => { this.$refs["leaderCoachForm"].validate(async (valid) => {
if (valid) { if (valid) {
let loading = this.$loading({ let loading = this.$loading({
lock: true, lock: true,
@@ -159,12 +159,13 @@ layout("/layouts/platform.html"){
</template> </template>
<template #edit_func> <template #edit_func>
<el-button @click="doSave" type="primary">保 存</el-button> <el-button :disabled="!activityReady" @click="doSave" type="primary">保 存</el-button>
<el-button @click="doOperate" type="primary">提 交</el-button> <el-button :disabled="!activityReady" @click="doOperate" type="primary">提 交</el-button>
</template> </template>
<template #edit> <template #edit>
<add-activity <add-activity
v-if="showActivity"
:form="formData" :form="formData"
:is_a06="is_A06" :is_a06="is_A06"
:is_club="is_club" :is_club="is_club"
@@ -174,12 +175,13 @@ layout("/layouts/platform.html"){
:unionid="'${@shiro.getPrincipalProperty('unit').getUnionid()}'" :unionid="'${@shiro.getPrincipalProperty('unit').getUnionid()}'"
@flip="flip" @flip="flip"
@flush="flush" @flush="flush"
@hook:mounted="activityMounted"
ref="activity" ref="activity"
></add-activity> ></add-activity>
</template> </template>
<template #view> <template #view>
<info ref="info"></info> <info v-if="showInfo" @hook:mounted="infoMounted" ref="info"></info>
</template> </template>
<send-msg :url="url" ref="sendMsg"></send-msg> <send-msg :url="url" ref="sendMsg"></send-msg>
@@ -203,6 +205,11 @@ layout("/layouts/platform.html"){
is_A06: false, is_A06: false,
is_h10: false, is_h10: false,
activities: [], activities: [],
showActivity: false,
activityReady: false,
activityAction: null,
showInfo: false,
infoId: null,
unitOptions: [], unitOptions: [],
activityTypeList: [], activityTypeList: [],
planList: [], planList: [],
@@ -317,27 +324,57 @@ layout("/layouts/platform.html"){
this.url = `<a href="` + (APP_DOMAIN + "/mobile/activity/Sports/list/activityList") + `">点击参与</a>` this.url = `<a href="` + (APP_DOMAIN + "/mobile/activity/Sports/list/activityList") + `">点击参与</a>`
this.$refs.sendMsg.openDialog(row.activityGroupId) this.$refs.sendMsg.openDialog(row.activityGroupId)
}, },
infoMounted() {
this.loadInfo()
},
loadInfo(id) {
if (id) {
this.infoId = id
}
if (this.$refs.info && this.infoId) {
const infoId = this.infoId
this.infoId = null
this.$refs.info.getInfo(infoId)
}
},
openView(row) { openView(row) {
const { id } = row const { id } = row
this.infoId = id
this.showInfo = true
this.$refs.guava.view() this.$refs.guava.view()
setTimeout(() => { this.$nextTick(() => this.loadInfo())
this.$refs.info.getInfo(id) },
}, 500) activityMounted() {
this.activityReady = true
this.handleActivityAction()
},
handleActivityAction(action) {
if (action) {
this.activityAction = action
}
if (!this.$refs.activity || !this.activityAction) {
return
}
const { type, row } = this.activityAction
this.activityAction = null
if (type === "add") {
this.$refs.activity.openAdd()
} else if (type === "edit") {
this.$refs.activity.openEdit(row, true)
}
}, },
async openAdd() { async openAdd() {
this.showActivity = true
this.$refs.guava.edit() this.$refs.guava.edit()
setTimeout(() => { this.handleActivityAction({ type: "add" })
this.$refs.activity.openAdd()
}, 500)
}, },
async openEdit(row) { async openEdit(row) {
const { id, applyStartTime, isSave } = row const { id, applyStartTime, isSave } = row
this.formData.id = id this.formData.id = id
const now = moment().valueOf() const now = moment().valueOf()
this.showActivity = true
this.$refs.guava.edit() this.$refs.guava.edit()
setTimeout(() => { this.handleActivityAction({ type: "edit", row })
this.$refs.activity.openEdit(row, true)
}, 500)
}, },
doDelete(row) { doDelete(row) {
const { id } = row const { id } = row
@@ -51,14 +51,18 @@ const activity = {
}) })
}, },
enterActivity(item) { enterActivity(item) {
if (item.type === "Activity" && "${@shiro.hasRole('sysadmin')||@shiro.hasRole('SchoolUnionAdmin')}||${@shiro.hasRole('gh01')||@shiro.hasRole('gh08')}" === 'false') { if (item.type === "Activity" && "${@shiro.hasRole('H04')||@shiro.hasRole('sysadmin')||@shiro.hasRole('SCHOOL_UNION_SPORTS_ADMIN')||@shiro.hasRole('gh01')||@shiro.hasRole('gh08')}" === "false") {
this.$message.warning("该活动只支持基层工会主席报名,个人报名请前往移动端") this.$message.warning("该活动只支持基层工会主席报名,个人报名请前往移动端")
return return
} }
if (item.type === "Activity" && "${@shiro.hasPermission('activity.apply')}" === "false") {
this.$message.warning("您没有体育活动报名权限")
return
}
if (item.url) { if (item.url) {
sublime.jumpPagePjax(item.url) sublime.jumpPagePjax(item.url)
} else if (item.url) { } else if (item.h5Url) {
this.$refs.openQRCode.openCode(item.url, "该活动只支持手机端报名,请使用手机扫描二维码报名!") this.$refs.openQRCode.openCode(item.h5Url, "该活动只支持手机端报名,请使用手机扫描二维码报名!")
} else { } else {
this.$message.warning("该活动不支持,请在手机上选择!") this.$message.warning("该活动不支持,请在手机上选择!")
} }
@@ -280,6 +280,17 @@ layout("/layouts/platform.html"){
> >
导入快递单号 导入快递单号
</el-button> </el-button>
<el-button
:disabled="!pageForm.projectId"
:loading="updateWelfareListUnitInfoLoading"
@click="updateWelfareListUnitInfo"
icon="el-icon-refresh"
size="small"
type="primary"
v-if="${@shiro.hasRole('sysadmin')}"
>
修改福利信息
</el-button>
<el-button <el-button
:disabled="!pageForm.projectId" :disabled="!pageForm.projectId"
@click="doOneKeyReceive(pageForm.projectId)" @click="doOneKeyReceive(pageForm.projectId)"
@@ -756,6 +767,8 @@ layout("/layouts/platform.html"){
shoppingTypeOptions: [], shoppingTypeOptions: [],
updateWelfareListUnitInfoLoading: false,
/** /**
* 添加福利名单相关 * 添加福利名单相关
*/ */
@@ -986,6 +999,35 @@ layout("/layouts/platform.html"){
this.$refs.expressExcelUpload.clearFiles() this.$refs.expressExcelUpload.clearFiles()
} }
}, },
/**
* 将当前福利项目名单中的工会和单位信息,同步为用户主档案中的最新信息。
*/
updateWelfareListUnitInfo() {
if (!this.pageForm.projectId) {
this.notifyWarning("请先选择福利项目")
return
}
this.$confirm("此操作将使用用户最新的工会和单位信息更新当前福利项目名单,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$set(this, "updateWelfareListUnitInfoLoading", true)
$.post(loc() + "/updateWelfareListUnitInfo", {
projectId: this.pageForm.projectId
}).then((resp) => {
if (resp.code === 0) {
this.notifySuccess("福利信息修改成功,共更新" + resp.data + "条名单")
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
}).always(() => {
this.$set(this, "updateWelfareListUnitInfoLoading", false)
})
}).catch(() => {
})
},
receiveAddressChange(address, optionIndex) { receiveAddressChange(address, optionIndex) {
this.$set(this.welfareOptions[optionIndex], "receiveAddress", address) this.$set(this.welfareOptions[optionIndex], "receiveAddress", address)
this.$forceUpdate() this.$forceUpdate()
@@ -127,8 +127,8 @@ layout("/layouts/platform.html"){
<el-button @click="openView(scope.row)" size="mini" type="primary">查看报名</el-button> <el-button @click="openView(scope.row)" size="mini" type="primary">查看报名</el-button>
<!--# } #--> <!--# } #-->
<!--# if(@shiro.hasRole('H04') || @shiro.hasRole('gh12')){ #--> <!--# if(@shiro.hasRole('H04') || @shiro.hasRole('gh12')){ #-->
<el-button @click="openImport(scope.row)" size="mini" type="primary">导入</el-button> <el-button :disabled="!isSignUpTime(scope.row)" @click="openImport(scope.row)" size="mini" type="primary">导入</el-button>
<el-button @click="openBm2(scope.row)" size="mini" type="primary">报名</el-button> <el-button :disabled="!isSignUpTime(scope.row)" @click="openBm2(scope.row)" size="mini" type="primary">报名</el-button>
<!--# } #--> <!--# } #-->
</template> </template>
</el-table-column> </el-table-column>
@@ -589,6 +589,12 @@ display: -webkit-box;
"file-import": httpVueLoader("/components/plugins/FileImport.vue") "file-import": httpVueLoader("/components/plugins/FileImport.vue")
}, },
methods: { methods: {
isSignUpTime(row) {
const now = new Date().getTime()
const bmkssj = moment(row.bmkssj.valueOf())
const bmjzsj = moment(row.bmjzsj.valueOf())
return bmkssj <= now && now < bmjzsj
},
openImport() { openImport() {
this.importDialog = true this.importDialog = true
this.$nextTick(() => { this.$nextTick(() => {