diff --git a/pom.xml b/pom.xml index 54367f9..a4d53b5 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ 1.12.1 1.9.1 4.2.0 - 9.4.47.v20220610 + 9.4.56.v20240826 https://budwk.com @@ -53,6 +53,12 @@ + + com.github.javen205 + IJPay-All + 2.9.10 + + com.xingyuv captcha-plus diff --git a/src/main/java/io/v/nutz/base/annontation/RepeatSubmit.java b/src/main/java/io/v/nutz/base/annontation/RepeatSubmit.java new file mode 100644 index 0000000..1c2e698 --- /dev/null +++ b/src/main/java/io/v/nutz/base/annontation/RepeatSubmit.java @@ -0,0 +1,23 @@ +package io.v.nutz.base.annontation; + +import java.lang.annotation.*; + +/** + * 自定义注解防止表单重复提交 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit { + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + public int interval() default 5000; + + /** + * 提示消息 + */ + public String message() default "请求过于频繁,请稍后再试"; +} \ No newline at end of file diff --git a/src/main/java/io/v/nutz/base/constant/RedisConstant.java b/src/main/java/io/v/nutz/base/constant/RedisConstant.java index 8978298..54137ba 100644 --- a/src/main/java/io/v/nutz/base/constant/RedisConstant.java +++ b/src/main/java/io/v/nutz/base/constant/RedisConstant.java @@ -18,8 +18,14 @@ public class RedisConstant { //企业微信TOKEN public final static String REDIS_KEY_QIYE_WECHAT_ACCESS_TOKEN = "qiyewx:token:"; + // 微信公众号TOKEN + public final static String REDIS_KEY_WECHAT_ACCESS_TOKEN = "wechat:token:"; //健步走小程序TOKEN public final static String REDIS_KEY_WE_APP_ACCESS_TOKEN = "weapp:token:"; + /** + * 接口重复提交前缀 + */ + public static final String REPEAT_SUBMIT_PREFIX = PLATFORM_REDIS_PREFIX + "repeat_submit:"; } diff --git a/src/main/java/io/v/nutz/base/exception/BaseException.java b/src/main/java/io/v/nutz/base/exception/BaseException.java new file mode 100644 index 0000000..e982c60 --- /dev/null +++ b/src/main/java/io/v/nutz/base/exception/BaseException.java @@ -0,0 +1,16 @@ +package io.v.nutz.base.exception; + +import cn.hutool.core.util.StrUtil; + +public class BaseException extends RuntimeException{ + + private static final long serialVersionUID = 7192152812384031563L; + + public BaseException(String message) { + super(message); + } + + public BaseException(String message, Object... arguments) { + super(StrUtil.format(message, arguments)); + } +} diff --git a/src/main/java/io/v/nutz/base/interceptor/repeatSubmit/RepeatSubmitAopConfiguration.java b/src/main/java/io/v/nutz/base/interceptor/repeatSubmit/RepeatSubmitAopConfiguration.java new file mode 100644 index 0000000..2aad748 --- /dev/null +++ b/src/main/java/io/v/nutz/base/interceptor/repeatSubmit/RepeatSubmitAopConfiguration.java @@ -0,0 +1,19 @@ +package io.v.nutz.base.interceptor.repeatSubmit; + +import io.v.nutz.base.annontation.RepeatSubmit; +import org.nutz.aop.MethodInterceptor; +import org.nutz.ioc.Ioc; +import org.nutz.ioc.aop.SimpleAopMaker; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.lang.reflect.Method; +import java.util.List; + +@IocBean(name = "$aop_repeatSubmit") +public class RepeatSubmitAopConfiguration extends SimpleAopMaker { + + @Override + public List makeIt(RepeatSubmit repeatSubmit, Method method, Ioc ioc) { + return List.of(new RepeatSubmitAopInterceptor(ioc, repeatSubmit, method)); + } +} diff --git a/src/main/java/io/v/nutz/base/interceptor/repeatSubmit/RepeatSubmitAopInterceptor.java b/src/main/java/io/v/nutz/base/interceptor/repeatSubmit/RepeatSubmitAopInterceptor.java new file mode 100644 index 0000000..d1800f3 --- /dev/null +++ b/src/main/java/io/v/nutz/base/interceptor/repeatSubmit/RepeatSubmitAopInterceptor.java @@ -0,0 +1,51 @@ +package io.v.nutz.base.interceptor.repeatSubmit; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import io.v.nutz.base.annontation.RepeatSubmit; +import io.v.nutz.base.constant.RedisConstant; +import io.v.nutz.base.exception.BaseException; +import io.v.nutz.web.commons.utils.ShiroUtil; +import org.nutz.aop.InterceptorChain; +import org.nutz.aop.MethodInterceptor; +import org.nutz.integration.jedis.RedisService; +import org.nutz.ioc.Ioc; +import org.nutz.mvc.Mvcs; + +import java.lang.reflect.Method; + +public class RepeatSubmitAopInterceptor implements MethodInterceptor { + + protected Ioc ioc; + + public RepeatSubmitAopInterceptor(Ioc ioc, RepeatSubmit repeatSubmit, Method method) { + RedisService redisService = ioc.get(RedisService.class); + } + + @Override + public void filter(InterceptorChain chain) throws Throwable { + Method method = chain.getCallingMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + int interval = annotation.interval(); + RedisService redisService = Mvcs.getIoc().get(RedisService.class); + String source = method.getDeclaringClass().getName() + "#" + method.getName(); + Object[] args = chain.getArgs(); + String parameterJson = ""; + if (ObjectUtil.isNotEmpty(args)) { + parameterJson = JSONUtil.toJsonStr(args); // 序列化实际参数 + } + String key = RedisConstant.REPEAT_SUBMIT_PREFIX + ShiroUtil.getUserId() + ":" + source; + + Boolean exists = redisService.exists(key); + if (exists) { + // 判断 + String oldParameterJson = redisService.get(key); + if (oldParameterJson != null && oldParameterJson.equalsIgnoreCase(parameterJson)) { + throw new BaseException(annotation.message()); + } + } else { + redisService.setex(key, interval / 1000, parameterJson); + } + chain.doChain(); + } +} diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/AlipayClientController.java b/src/main/java/io/v/nutz/base/payment/alipay/controller/AlipayClientController.java similarity index 94% rename from src/main/java/io/v/nutz/zhgh/memberCost/controller/AlipayClientController.java rename to src/main/java/io/v/nutz/base/payment/alipay/controller/AlipayClientController.java index 7270989..3a20c7c 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/controller/AlipayClientController.java +++ b/src/main/java/io/v/nutz/base/payment/alipay/controller/AlipayClientController.java @@ -1,6 +1,6 @@ -package io.v.nutz.zhgh.memberCost.controller; +package io.v.nutz.base.payment.alipay.controller; -import io.v.nutz.zhgh.memberCost.service.AlipayService; +import io.v.nutz.base.payment.alipay.service.AlipayService; import lombok.extern.slf4j.Slf4j; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/AlipayClientConfig.java b/src/main/java/io/v/nutz/base/payment/alipay/service/AlipayClientConfig.java similarity index 98% rename from src/main/java/io/v/nutz/zhgh/memberCost/controller/AlipayClientConfig.java rename to src/main/java/io/v/nutz/base/payment/alipay/service/AlipayClientConfig.java index 7819815..243fa05 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/controller/AlipayClientConfig.java +++ b/src/main/java/io/v/nutz/base/payment/alipay/service/AlipayClientConfig.java @@ -1,4 +1,4 @@ -package io.v.nutz.zhgh.memberCost.controller; +package io.v.nutz.base.payment.alipay.service; /** * @ClassName AlipayClientConfig diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/service/AlipayService.java b/src/main/java/io/v/nutz/base/payment/alipay/service/AlipayService.java similarity index 87% rename from src/main/java/io/v/nutz/zhgh/memberCost/service/AlipayService.java rename to src/main/java/io/v/nutz/base/payment/alipay/service/AlipayService.java index 32da08e..255d7db 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/service/AlipayService.java +++ b/src/main/java/io/v/nutz/base/payment/alipay/service/AlipayService.java @@ -1,4 +1,4 @@ -package io.v.nutz.zhgh.memberCost.service; +package io.v.nutz.base.payment.alipay.service; import io.v.nutz.base.service.ViService; diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/service/impl/AlipayNewServiceImpl.java b/src/main/java/io/v/nutz/base/payment/alipay/service/impl/AlipayNewServiceImpl.java similarity index 96% rename from src/main/java/io/v/nutz/zhgh/memberCost/service/impl/AlipayNewServiceImpl.java rename to src/main/java/io/v/nutz/base/payment/alipay/service/impl/AlipayNewServiceImpl.java index 0dd726e..2c99caa 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/service/impl/AlipayNewServiceImpl.java +++ b/src/main/java/io/v/nutz/base/payment/alipay/service/impl/AlipayNewServiceImpl.java @@ -1,4 +1,4 @@ -package io.v.nutz.zhgh.memberCost.service.impl; +package io.v.nutz.base.payment.alipay.service.impl; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayConstants; @@ -7,11 +7,11 @@ import com.alipay.api.domain.AlipayTradeWapPayModel; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.AlipayTradeWapPayRequest; import com.alipay.api.response.AlipayTradeWapPayResponse; +import io.v.nutz.base.payment.alipay.service.AlipayClientConfig; +import io.v.nutz.base.payment.alipay.service.AlipayService; import io.v.nutz.base.service.impl.ViServiceImpl; import io.v.nutz.web.commons.utils.ShiroUtil; -import io.v.nutz.zhgh.memberCost.controller.AlipayClientConfig; import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo; -import io.v.nutz.zhgh.memberCost.service.AlipayService; import lombok.extern.slf4j.Slf4j; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; diff --git a/src/main/java/io/v/nutz/base/payment/enums/RefundStateEnum.java b/src/main/java/io/v/nutz/base/payment/enums/RefundStateEnum.java new file mode 100644 index 0000000..33b6b38 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/enums/RefundStateEnum.java @@ -0,0 +1,24 @@ +package io.v.nutz.base.payment.enums; + +import io.v.nutz.base.annontation.SelectEnum; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 退款状态枚举 + */ +@Getter +@SelectEnum +@AllArgsConstructor +public enum RefundStateEnum { + + INITIATE_REFUND("发起退款"), + REFUNDING("退款中"), + REFUND_SUCCESS("退款成功"), + REFUND_FAILED("退款失败"); + + /** + * 避免和枚举类的name冲突 + */ + public final String refundStateName; +} diff --git a/src/main/java/io/v/nutz/base/payment/enums/TradingChannelEnum.java b/src/main/java/io/v/nutz/base/payment/enums/TradingChannelEnum.java new file mode 100644 index 0000000..9939c3b --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/enums/TradingChannelEnum.java @@ -0,0 +1,25 @@ +package io.v.nutz.base.payment.enums; + +import io.v.nutz.base.annontation.SelectEnum; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 交易渠道枚举 + */ +@Getter +@SelectEnum +@AllArgsConstructor +public enum TradingChannelEnum { + + QQ_PAY("QQ"), + WX_PAY("微信"), + JD_PAY("京东"), + ALI_PAY("支付宝"), + UNION_PAY("云闪付"); + + /** + * 避免和枚举类的name冲突 + */ + public final String trdingChannelName; +} diff --git a/src/main/java/io/v/nutz/base/payment/enums/TradingStateEnum.java b/src/main/java/io/v/nutz/base/payment/enums/TradingStateEnum.java new file mode 100644 index 0000000..95703dc --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/enums/TradingStateEnum.java @@ -0,0 +1,24 @@ +package io.v.nutz.base.payment.enums; + +import io.v.nutz.base.annontation.SelectEnum; +import lombok.AllArgsConstructor; +import lombok.Getter; + + +/** + * 支付状态枚举 + */ +@Getter +@SelectEnum +@AllArgsConstructor +public enum TradingStateEnum { + + WAITING_FOR_PAYMENT("等待支付"), + PAY_SUCCESS("支付成功"), + CANCEL_PAYMENT("取消支付"); + + /** + * 避免和枚举类的name冲突 + */ + public final String trdingStateName; +} diff --git a/src/main/java/io/v/nutz/base/payment/enums/TradingTypeEnum.java b/src/main/java/io/v/nutz/base/payment/enums/TradingTypeEnum.java new file mode 100644 index 0000000..174dd6a --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/enums/TradingTypeEnum.java @@ -0,0 +1,22 @@ +package io.v.nutz.base.payment.enums; + +import io.v.nutz.base.annontation.SelectEnum; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 交易类型枚举 + */ +@Getter +@SelectEnum +@AllArgsConstructor +public enum TradingTypeEnum { + + PAYMENT("付款"), + REFUND("退款"); + + /** + * 避免和枚举类的name冲突 + */ + public final String trdingTypeName; +} diff --git a/src/main/java/io/v/nutz/base/payment/enums/WxPayResultCode.java b/src/main/java/io/v/nutz/base/payment/enums/WxPayResultCode.java new file mode 100644 index 0000000..2a9e741 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/enums/WxPayResultCode.java @@ -0,0 +1,30 @@ +package io.v.nutz.base.payment.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @version 1.0 + * @Author zzr + * @name:WxPayResultCode + * @Date 2025/3/10 10:27 + * @注释 微信支付返回码 + */ +@Getter +@AllArgsConstructor +public enum WxPayResultCode { + + SUCCESS(200, "操作成功"), + APPID_MCHID_NOT_MATCH(400, "商户ID和服务号或企业微信号不匹配"), + INVALID_REQUEST(400, "无效请求"), + MCH_NOT_EXISTS(400, "商户号不存在"), + PARAM_ERROR(400, "参数错误"), + SIGN_ERROR(401, "签名错误"), + NO_AUTH(403, "商户无权限"), + OUT_TRADE_NO_USED(403, "商户订单号重复"), + FREQUENCY_LIMITED(429, "频率超限"), + SYSTEM_ERROR(500, "系统错误"); + + final int code; + final String msg; +} diff --git a/src/main/java/io/v/nutz/base/payment/model/PaymentRefundRecord.java b/src/main/java/io/v/nutz/base/payment/model/PaymentRefundRecord.java new file mode 100644 index 0000000..105940f --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/model/PaymentRefundRecord.java @@ -0,0 +1,59 @@ +package io.v.nutz.base.payment.model; + +import io.v.nutz.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.*; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +/** + * 退款记录表 + */ +@Data +@Table +@Comment("退款记录表") +@EqualsAndHashCode(callSuper = true) +public class PaymentRefundRecord extends BaseModel { + + @Column + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("关联系统交易订单表Id") + @ColDefine(type = ColType.VARCHAR,width = 32) + private String paymentTransactionId; + + @Column + @Comment("本次退款订单号") + @ColDefine(type = ColType.VARCHAR,width = 128) + private String outRefundTradeNo; + + @Column + @Comment("第三方支付的退款单号") + @ColDefine(type = ColType.VARCHAR,width = 128) + private String refundNo; + + @Column + @Comment("退款状态") + @ColDefine(type = ColType.VARCHAR,width = 30) + private String refundState; + + @Column + @Comment("本次退款金额") + @ColDefine(type = ColType.INT) + private Integer refundAmount; + + @Column + @Comment("原订单金额") + @ColDefine(type = ColType.INT) + private Integer total; + + @Column + @Comment("交易返回数据") + @ColDefine(type = ColType.MYSQL_JSON) + private String resultJson; +} diff --git a/src/main/java/io/v/nutz/base/payment/model/PaymentTransaction.java b/src/main/java/io/v/nutz/base/payment/model/PaymentTransaction.java new file mode 100644 index 0000000..58f1cca --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/model/PaymentTransaction.java @@ -0,0 +1,129 @@ +package io.v.nutz.base.payment.model; + +import io.v.nutz.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.*; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +/** + * 交易单表 + */ +@Data +@Table +@Comment("交易订单表") +@EqualsAndHashCode(callSuper = true) +public class PaymentTransaction extends BaseModel { + + @Column + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("商户号") + @ColDefine(type = ColType.VARCHAR,width = 128) + private String mchId; + + @Column + @Comment("订单号,传递给交易系统,交易系统订单号") + @ColDefine(type = ColType.VARCHAR,width = 128) + private String outTradeNo; + + @Column + @Comment("交易系统交易号") + @ColDefine(type = ColType.VARCHAR,width = 128) + private String transactionId; + + @Column + @Comment("交易渠道") + @ColDefine(type = ColType.VARCHAR,width = 50) + private String tradingChannel; + + @Column + @Comment("交易类型") + @ColDefine(type = ColType.VARCHAR,width = 30) + private String tradingType; + + @Column + @Comment("交易状态") + @ColDefine(type = ColType.VARCHAR,width = 50) + private String tradingState; + + @Column + @Comment("用户ID") + @ColDefine(type = ColType.VARCHAR,width = 32) + private String userId; + + @Column + @Comment("用户工号") + @ColDefine(type = ColType.VARCHAR,width = 50) + private String loginName; + + @Column + @Comment("用户姓名") + @ColDefine(type = ColType.VARCHAR,width = 50) + private String userName; + + @Column + @Comment("工会id") + @ColDefine(type = ColType.VARCHAR,width = 32) + private String unionId; + + @Column + @Comment("用户工会") + @ColDefine(type = ColType.VARCHAR,width = 50) + private String unionName; + + @Column + @Comment("单位id") + @ColDefine(type = ColType.VARCHAR,width = 32) + private String unitId; + + @Column + @Comment("用户单位") + @ColDefine(type = ColType.VARCHAR,width = 50) + private String unitName; + + @Column + @Comment("交易金额") + @ColDefine(type = ColType.INT) + private Integer tradingAmount; + + @Column + @Comment("是否退款") + @ColDefine(type = ColType.BOOLEAN) + private Boolean isRefund; + + @Column + @Comment("退款金额") + @ColDefine(type = ColType.INT) + private Integer refundAmount; + + @Column + @Comment("订单描述") + @ColDefine(type = ColType.VARCHAR,width = 128) + private String description; + + @Column + @Comment("附加详细描述") + @ColDefine(type = ColType.TEXT) + private String attach; + + @Column + @Comment("微信支付用户openId") + @ColDefine(type = ColType.VARCHAR,width = 50) + private String openId; + + @Column + @Comment("交易返回数据") + @ColDefine(type = ColType.MYSQL_JSON) + private String resultJson; + + @Column + @Comment("交易名单的主键id") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String memberPaymentInfoId; +} diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/bean/WxPayV3Bean.java b/src/main/java/io/v/nutz/base/payment/wxpay/bean/WxPayV3Bean.java new file mode 100644 index 0000000..2f9c1e0 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/bean/WxPayV3Bean.java @@ -0,0 +1,53 @@ +package io.v.nutz.base.payment.wxpay.bean; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @version 1.0 + * @Author zzr + * @name:WxPayV3Bean + * @Date 2025/3/6 9:54 + * @注释 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WxPayV3Bean { + + /** + * 服务号 + * appid: wxf12fa6175ec7c75f + * app_secret: f65bbb41d3de4a93c8c93d8e2fd50369 + */ + + private String appId; + private String keyPath; + private String publicKeyPath; + private String certPath; + private String certP12Path; + private String platformCertPath; + private String mchId; + private String apiKey; + private String apiKey3; + private String domain; + private String publicKeyId; + + @Override + public String toString() { + return "WxPayV3Bean{" + + "appId='" + appId + '\'' + + ", keyPath='" + keyPath + '\'' + + ", publicKeyPath='" + publicKeyPath + '\'' + + ", certPath='" + certPath + '\'' + + ", certP12Path='" + certP12Path + '\'' + + ", platformCertPath='" + platformCertPath + '\'' + + ", mchId='" + mchId + '\'' + + ", apiKey='" + apiKey + '\'' + + ", apiKey3='" + apiKey3 + '\'' + + ", domain='" + domain + '\'' + + ", publicKeyId='" + publicKeyId + '\'' + + '}'; + } +} diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/controller/WxPayCommonController.java b/src/main/java/io/v/nutz/base/payment/wxpay/controller/WxPayCommonController.java new file mode 100644 index 0000000..2fbb068 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/controller/WxPayCommonController.java @@ -0,0 +1,42 @@ +package io.v.nutz.base.payment.wxpay.controller; + +import io.v.nutz.base.payment.wxpay.service.WxPayCommonService; +import lombok.extern.slf4j.Slf4j; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @version 1.0 + * @Author zzr + * @name:WxPayCommonController + * @Date 2025/3/10 19:28 + * @注释 + */ +@Slf4j +@IocBean +@Ok("json:full") +@At("/platform/wxPay") +public class WxPayCommonController { + + @Inject + private WxPayCommonService wxPayCommonService; + + + @At + @Ok("void") + public void payNotify(HttpServletRequest request, HttpServletResponse response){ + wxPayCommonService.payNotify(request, response); + } + + + @At + @Ok("void") + public void refundNotify(HttpServletRequest request, HttpServletResponse response){ + wxPayCommonService.refundNotify(request, response); + } +} diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_cert.pem b/src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_cert.pem new file mode 100644 index 0000000..ee557fe --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEKDCCAxCgAwIBAgIUONYpzvxVfV/RrWnLGPlL6/nb/AcwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjUwMzA2MTQwNDAzWhcNMzAwMzA1MTQwNDAzWjCBgTETMBEGA1UEAwwK +MTY2NjkzMDk4MDEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL +DCTmrabmsYnlnLDlpKfkuInmgJ3np5HmioDmnInpmZDlhazlj7gxCzAJBgNVBAYT +AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALlDML+FJEW+m2hXp/qUH1cU6xlwFllLU4LjFusJkxwyzzcZO4nY0mOi +3woz8xIJipvUWj2Y413WYwMkfcJcRgghupZkfYwdQtSyPfUu7xQBUm8TLWGH88Re +kyyVABEB5T+ne6r3LesXS4IuZknm5N/iaa4z9mbbscGqF0GLvMUqZCRNyTkyfK9r +Bkvs/vspi+Jcjx9I7aU++h/vwzsGB6Bf/uB3kLMb+lj/i3xY20sdrrW8JD2sqONQ +NcRIKmNXSp4zsNy+d6kIKWE3P5609ZTta1ogkeGBNdGXMRNNy6vLIudQc7zny/9H +S4LxGsIrsYmZNhGljRng56X+PG3w1CUCAwEAAaOBuTCBtjAJBgNVHRMEAjAAMAsG +A1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2Y2Eu +aXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRC +MDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJCMjdB +OUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQBQ5e8U +1pQ40gfRoZyO0s+QF16L8pAcVLVwHffo0mmJpIRB4bu8W5IJ9kKhksJ0CzZ0YEwB +Yv7/LTC1qpi7PE73nZ4Ytm9oZeH1Kk5KIlQS+2H3IwOpoV4wucMc34y6MVQJjC7E +u5OnglBPSAopwNP4p+Us9A3ah7Ks48XCw+LCEWpA/setJwo9C13Qq2AQm8qLpogK +pNBwm/oylxb8/kZK1lLPgcXd7geLDhl/Str+aOzBXyd7YSEMxS8zWiZhWozu6rCq +dg10QFKicPGDkDzNQwBz7cDXvlJXSWRDBK5r1li1YgX+QD5WJYPchATo5SlvF4Lr +5/V/dZzI7BJTinr1 +-----END CERTIFICATE----- diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_key.pem b/src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_key.pem new file mode 100644 index 0000000..36b4b67 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5QzC/hSRFvpto +V6f6lB9XFOsZcBZZS1OC4xbrCZMcMs83GTuJ2NJjot8KM/MSCYqb1Fo9mONd1mMD +JH3CXEYIIbqWZH2MHULUsj31Lu8UAVJvEy1hh/PEXpMslQARAeU/p3uq9y3rF0uC +LmZJ5uTf4mmuM/Zm27HBqhdBi7zFKmQkTck5MnyvawZL7P77KYviXI8fSO2lPvof +78M7BgegX/7gd5CzG/pY/4t8WNtLHa61vCQ9rKjjUDXESCpjV0qeM7DcvnepCClh +Nz+etPWU7WtaIJHhgTXRlzETTcuryyLnUHO858v/R0uC8RrCK7GJmTYRpY0Z4Oel +/jxt8NQlAgMBAAECggEBAKGQqTi9Ii741Ysftsze1J4bDhS3HaZt/LyO0vPS+8qB +vRiuFrBWuNRPu/en6nl2fLz9al2Bsfep+dtJvar80c5Rq+lwZdUFx7y6XTIv5iwq +IvvfW5pM+Bfu6qbcopwybP0gUMd+nTsCq8UyNd6JUFy3lbZodqCpQ9JDRdjRxqSi +oMonve36Q9kJyDs0mS2F0w+Y9oYFtpE9WvGMBthwT7VfzWuDa+YC/Gh0RhT2V9OG +uDC8kxmfaIbk0IITDVFWVMMhScXiqmJL1II9yQWsCHH73dZS+dDxwwH0ObRD3kOx +hHptxpRzhtuNxLjCOZDGhneyvUHIftpqf4wdOgQ8iUECgYEA6TDo36s3XDFIs8SH +lpVYrhie0nmaNET50CtOc5C2M1OotrkMUrQTMoa4F263UloJfqweW9AKZX11wPp2 +8h7af2sZsGawjuwv7mMthZJoi0fc5J2+z6BnVGgFo7uZbcs1Y/+B0tNrDKIHVrqb +Lywmh18iKDXrMiMDMRp8mtiNyU0CgYEAy2Im0oGb1rmAh9gPvQIENmNEgRsyIU4x +WPAFb5KO7jnseO9KrC9uiRSO5JuwZu/QNGlga2w77Gu7n+Q4HfSU2KFlNhR6pKjb +mqXzPq1quryzbtWay520VsRKz3F/Tfp1FUpIuZvj8x7FrL8z8vjbLkbW4p+Z7HE4 +heYdmk2KCjkCgYBC6xU48pJtc3swk2pxjSQoucoCDoFxFIY39eWKhnGy4wQcg5gE +t8Z2jnCP0ObszAfDgTMpF9eT7l/3ZCj0LGR5gkuZ8q4iJadPmsh6d1xm89VxCc+z +F43vi/ZqWTCbcGA+9RjVXnRu6KSAgAZ+kAXyfRt5PJflCGUnFznA2fo+VQKBgG6s +n7Gno/UKBv5lPdM9mKs8A5g6A2KKWWIrYtnfJ/ZNAPiiRsFCImVoEYYnjqvFTkhn +G3f5cvZpfbU5jKwIX0MvOJNgO4kE9lPGgIKX/d9WNlJW3KB21h7tPER8X6lAZJxM +SxztzvKKyUkC1zMd5q/dDqxN64X16qs6mNtR3I9JAoGBAJbqQtg59Szi2m4jZ9Xo +0cKvP7+PSKgysdWsemTkU0ji1rcMpGRTtcUGRWlawLjRSInQS+xNdL6GVU5mHZWe +gNwXIXEpape4IJta0B1PYAhNcIvOSh5CzcUnVTeo6q0CTSVQPdsivlrObLKZ4uGQ +eewDw82Ej6YoSgczjf99ug0x +-----END PRIVATE KEY----- diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/pem/wechatpay.pem b/src/main/java/io/v/nutz/base/payment/wxpay/pem/wechatpay.pem new file mode 100644 index 0000000..aaa6938 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/pem/wechatpay.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEFDCCAvygAwIBAgIUKN05Gm0Q9n9mLPPltUh8qjKKegQwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjUwMzA2MTQwNDAzWhcNMzAwMzA1MTQwNDAzWjBuMRgwFgYDVQQDDA9U +ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl +bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGEwJDTjERMA8GA1UEBwwIU2hlblpo +ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbMenF0GNlcUYErPpj +Aj+WOuzRFz+PJcrtgkI81S0WDh8flMtq1A2nwKARNyPXx+UPd9it657nH5G8E9kL +Ea5u5rENpn+JZc4cAIvZqazteSAUg2pfQH9ewW+/CxNOWarjXPNRYa0OjZWf+5F8 +ZBOT/x8sJ2+jou9lxKNe1skha+jzmiQ8m3B2vPXqR87qgL/fyQjjpLvBGpvearh/ +hxTMBEfGeB4F6Fu52Fz3/VCWe38ubxwj8EMcRSPp4xsl2BTaRVMMT525Hul2N49C +vXCSVsCNt3ND8UDffXnb19Zgpir4KqpECTbfqcT7YX+U34P1Av8gcDl6rRm+7Wpj +PY4RAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB +kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv +aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4 +RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0 +MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAUAvXCx55ZGmd3ATi28W4mirnUHQb2RAw +X5/0OC2SkuBdzxI5RbY4YAuugP6nQIAU8TXCANzno2oqZ6vmFZ4dBnJmLewVFMoM +qp18A4RWmnsGnmEIgMm6N1kB+RbM11oLCLdy2KK0SvmqDmTBFJyjyUGqEmRshnV/ +QfKn4UUzJ4suDFAolGX4DyDIOtM0D+SCHwwxGI4yf2+nj4IB8XgmmOeschxoSvu9 +T3E6BgQ271YVDwg+3r9CiFVI5JdyftvQagOEQmZJMPINEKgFn5LzrZJW7mH42+N+ +i4RFz04wpGUsrSFDESh3JxYSNhq/HulpMLinntWxEBHFhpkjyyGDGg== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/service/WxPayCommonService.java b/src/main/java/io/v/nutz/base/payment/wxpay/service/WxPayCommonService.java new file mode 100644 index 0000000..bc29ee1 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/service/WxPayCommonService.java @@ -0,0 +1,70 @@ +package io.v.nutz.base.payment.wxpay.service; + +import io.v.nutz.base.payment.wxpay.vo.WxPayNeedParamVo; +import io.v.nutz.base.result.Result; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @version 1.0 + * @Author zzr + * @name:WxPayCommonService + * @Date 2025/3/6 11:40 + * @注释 + */ +public interface WxPayCommonService { + + + /** + * 二维码统一下单,返回支付二维码链接 + * @param paramVo 需要传递的信息,请自定义付款金额Amount 和 付款描述Description,其他关键信息请勿修改 + * 注意Amount的总价total是int类型,会自动乘以0.01,例如传递100元,会自动转换为1元 + * @return + */ + Result nativePay(WxPayNeedParamVo paramVo); + + + /** + * JSAPI支付,返回支付参数 + * @param paramVo 需要传递的信息,请自定义付款金额Amount 和 付款描述Description,其他关键信息请勿修改 + * 注意Amount的总价total是int类型,会自动乘以0.01,例如传递100元,会自动转换为1元 + * JsAPI支付,需要传递openId,请自行获取 + * @return + */ + Result jsApiPay(WxPayNeedParamVo paramVo); + + /** + * WAP支付,返回支付参数 + * @param paramVo 需要传递的信息,请自定义付款金额Amount 和 付款描述Description,其他关键信息请勿修改 + * 注意Amount的总价total是int类型,会自动乘以0.01,例如传递100元,会自动转换为1元 + * @return + */ + Result wapPay(WxPayNeedParamVo paramVo); + + + /** + * 支付回调,验证是否支付成功 + * @param request 请求 + * @param response 响应 + */ + void payNotify(HttpServletRequest request, HttpServletResponse response); + + + /** + * 退款 + * @param transactionId 微信订单号 + * @param outTradeNo 商户订单号 + * @return + */ + Result refund(String transactionId, String outTradeNo); + + + /** + * 退款回调 + * @param request 请求 + * @param response 响应 + */ + void refundNotify(HttpServletRequest request, HttpServletResponse response); + +} diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/service/impl/WxPayCommonServiceImpl.java b/src/main/java/io/v/nutz/base/payment/wxpay/service/impl/WxPayCommonServiceImpl.java new file mode 100644 index 0000000..e3f122b --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/service/impl/WxPayCommonServiceImpl.java @@ -0,0 +1,599 @@ +package io.v.nutz.base.payment.wxpay.service.impl; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.ContentType; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.ijpay.core.IJPayHttpResponse; +import com.ijpay.core.enums.AuthTypeEnum; +import com.ijpay.core.enums.RequestMethodEnum; +import com.ijpay.core.kit.AesUtil; +import com.ijpay.core.kit.HttpKit; +import com.ijpay.core.kit.PayKit; +import com.ijpay.core.kit.WxPayKit; +import com.ijpay.wxpay.WxPayApi; +import com.ijpay.wxpay.enums.WxDomainEnum; +import com.ijpay.wxpay.enums.v3.BasePayApiEnum; +import com.ijpay.wxpay.model.v3.*; +import io.v.nutz.base.payment.enums.*; +import io.v.nutz.base.payment.model.PaymentRefundRecord; +import io.v.nutz.base.payment.model.PaymentTransaction; +import io.v.nutz.base.payment.wxpay.bean.WxPayV3Bean; +import io.v.nutz.base.payment.wxpay.service.WxPayCommonService; +import io.v.nutz.base.payment.wxpay.vo.WxPayNeedParamVo; +import io.v.nutz.base.result.Result; +import io.v.nutz.sys.models.Sys_user; +import io.v.nutz.sys.models.User; +import io.v.nutz.sys.services.SysUserService; +import io.v.nutz.web.commons.utils.ShiroUtil; +import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo; +import lombok.extern.slf4j.Slf4j; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.random.R; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.FileWriter; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Map; + +/** + * @version 1.0 + * @Author zzr + * @name:WxPayCommonServiceImpl + * @Date 2025/3/6 11:41 + * @注释 + */ +@Slf4j +@IocBean +public class WxPayCommonServiceImpl implements WxPayCommonService { + + // 判断下单接口成功返回码 + private final static int OK = 200; + + // 1666930980 商户号 + // ww3eda050a1b5ad96e 企业微信 + // wxf12fa6175ec7c75f 微信服务号 + // 微信V3必要的支付参数,后期优化 + private final static WxPayV3Bean WX_PAY_V3_BEAN = new WxPayV3Bean( + "ww3eda050a1b5ad96e", + "src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_key.pem", + "src/main/java/io/v/nutz/base/payment/wxpay/wxpay/pem/apiclient_key.pem", + "src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_cert.pem", + "src/main/java/io/v/nutz/base/payment/wxpay/pem/apiclient_cert.pem", + "src/main/java/io/v/nutz/base/payment/wxpay/pem/wechatpay.pem", + "1666930980", + "", + "4ebd020883285d698c44ec50939c0967", + "https://schoolhd.top/platform", + ""); + + // 自定义参数,内容是统一的,但会被多次调用,直接声明 + private String serialNo; + private String platSerialNo; + private final String payNotifyUrl = WX_PAY_V3_BEAN.getDomain().concat("/wxPay/payNotify"); + private final String refundNotifyUrl = WX_PAY_V3_BEAN.getDomain().concat("/wxPay/refundNotify"); + + @Inject + private Dao dao; + @Inject + private SysUserService sysUserService; + + /** + * 微信扫描二维码支付 + * @param paramVo 需要传递的信息,请自定义付款金额Amount 和 付款描述Description,其他关键信息请勿修改 + * 注意Amount的总价total是int类型,会自动乘以0.01,例如传递100元,会自动转换为1元 + * @return + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public Result nativePay(WxPayNeedParamVo paramVo) { + try { + // 商户订单号,存储当前用户的订单 + String outTradeNo = PayKit.generateStr(); + + String timeExpire = getNowTimeExpire(); + UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() + .setAppid(WX_PAY_V3_BEAN.getAppId()) + .setMchid(WX_PAY_V3_BEAN.getMchId()) + // 描述信息 + .setDescription(paramVo.getDescription()) + .setOut_trade_no(outTradeNo) + .setTime_expire(timeExpire) + .setAttach(paramVo.getAttach()) + .setNotify_url(payNotifyUrl) + // 订单金额 + .setAmount(paramVo.getAmount()); + + log.info("二维码支付统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); + IJPayHttpResponse response = WxPayApi.v3( + RequestMethodEnum.POST, + WxDomainEnum.CHINA.toString(), + BasePayApiEnum.NATIVE_PAY.toString(), + WX_PAY_V3_BEAN.getMchId(), + getSerialNumber(), + null, + WX_PAY_V3_BEAN.getKeyPath(), + JSONUtil.toJsonStr(unifiedOrderModel), + AuthTypeEnum.RSA.getCode() + ); + log.info("统一下单响应 {}", response); + // 根据证书序列号查询对应的证书来验证签名结果 + boolean verifySignature = WxPayKit.verifySignature(response, WX_PAY_V3_BEAN.getPlatformCertPath()); + log.info("verifySignature: {}", verifySignature); + // 下单返回数据 + String result = response.getBody(); + + JSONObject jsonObject = JSONUtil.parseObj(result); + + if (response.getStatus() == OK) { + // 统一下单成功,向数据库插入订单记录 + insertTradingOrder(paramVo, outTradeNo, result); + return Result.success("请在三分钟内完成支付,超时将自动取消订单").addData(result); + } else { + WxPayResultCode code = WxPayResultCode.valueOf(WxPayResultCode.class, jsonObject.getStr("code")); + return Result.error(code.getMsg()); + } + } catch (Exception e) { + log.error("系统异常", e); + return Result.error(e.getMessage()); + } + } + + + @Override + @Aop(TransAop.READ_COMMITTED) + public Result jsApiPay(WxPayNeedParamVo paramVo) { + try { + Sys_user user = dao.fetch(Sys_user.class, ShiroUtil.getUserId()); + if (StrUtil.isBlank(user.getOpenId())) { + return Result.error("未获取到当前用户的openid,请重新登录"); + } + // 商户订单号,存储当前用户的订单 + String outTradeNo = PayKit.generateStr(); + // 订单关闭时间 + String timeExpire = getNowTimeExpire(); + UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() + .setAppid(WX_PAY_V3_BEAN.getAppId()) + .setMchid(WX_PAY_V3_BEAN.getMchId()) + .setDescription(paramVo.getDescription()) + .setOut_trade_no(outTradeNo) + .setTime_expire(timeExpire) + .setAttach(paramVo.getAttach()) + .setNotify_url(payNotifyUrl) + .setAmount(paramVo.getAmount()) + .setPayer(new Payer().setOpenid(user.getOpenId())); + + log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); + IJPayHttpResponse response = WxPayApi.v3( + RequestMethodEnum.POST, + WxDomainEnum.CHINA.toString(), + BasePayApiEnum.JS_API_PAY.toString(), + WX_PAY_V3_BEAN.getMchId(), + getSerialNumber(), + null, + WX_PAY_V3_BEAN.getKeyPath(), + JSONUtil.toJsonStr(unifiedOrderModel) + ); + log.info("统一下单响应 {}", response); + // 根据证书序列号查询对应的证书来验证签名结果 + boolean verifySignature = WxPayKit.verifySignature(response, WX_PAY_V3_BEAN.getPlatformCertPath()); + log.info("verifySignature: {}", verifySignature); + if (response.getStatus() == OK && verifySignature) { + String body = response.getBody(); + JSONObject jsonObject = JSONUtil.parseObj(body); + String prepayId = jsonObject.getStr("prepay_id"); + Map map = WxPayKit.jsApiCreateSign(WX_PAY_V3_BEAN.getAppId(), prepayId, WX_PAY_V3_BEAN.getKeyPath()); + log.info("唤起支付参数:{}", map); + // 唤起支付成功,向数据库插入订单参数 + insertTradingOrder(paramVo, outTradeNo, body); + return Result.success(); + } +// return JSONUtil.toJsonStr(response); + return null; + } catch (Exception e) { + log.error("系统异常", e); + return Result.error(e.getMessage()); + } + } + + + @Override + @Aop(TransAop.READ_COMMITTED) + public Result wapPay(WxPayNeedParamVo paramVo) { + try { + // 商户订单号,存储当前用户的订单 + String outTradeNo = PayKit.generateStr(); + // 订单关闭时间 + String timeExpire = getNowTimeExpire(); + UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() + .setAppid(WX_PAY_V3_BEAN.getAppId()) + .setMchid(WX_PAY_V3_BEAN.getMchId()) + .setDescription(paramVo.getDescription()) + .setOut_trade_no(outTradeNo) + .setTime_expire(timeExpire) + .setAttach(paramVo.getAttach()) + .setNotify_url(payNotifyUrl) + .setAmount(paramVo.getAmount()) + .setScene_info( + new SceneInfo() + .setPayer_client_ip(""). + setH5_info(new H5Info() + .setType("Wap"))); + + log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); + IJPayHttpResponse response = WxPayApi.v3( + RequestMethodEnum.POST, + WxDomainEnum.CHINA.toString(), + BasePayApiEnum.H5_PAY.toString(), + WX_PAY_V3_BEAN.getMchId(), + getSerialNumber(), + null, + WX_PAY_V3_BEAN.getKeyPath(), + JSONUtil.toJsonStr(unifiedOrderModel) + ); + log.info("统一下单响应 {}", response); + // 根据证书序列号查询对应的证书来验证签名结果 + boolean verifySignature = WxPayKit.verifySignature(response, WX_PAY_V3_BEAN.getPlatformCertPath()); + log.info("verifySignature: {}", verifySignature); + if (response.getStatus() == OK && verifySignature) { + String body = response.getBody(); + insertTradingOrder(paramVo, outTradeNo, body); + return Result.success(JSONUtil.toJsonStr(body)); + } +// return JSONUtil.toJsonStr(response); + return Result.error(); + } catch (Exception e) { + log.error("系统异常", e); + return Result.error(e.getMessage()); + } + } + + + /** + * 支付回调 + * @param request + * @param response + */ + @Override + public void payNotify(HttpServletRequest request, HttpServletResponse response) { + Map map = new HashMap<>(12); + try { + String timestamp = request.getHeader("Wechatpay-Timestamp"); + String nonce = request.getHeader("Wechatpay-Nonce"); + String serialNo = request.getHeader("Wechatpay-Serial"); + String signature = request.getHeader("Wechatpay-Signature"); + + log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature); + String result = HttpKit.readData(request); + log.info("支付通知密文 {}", result); +// String plainText = null; + // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号 + String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, + WX_PAY_V3_BEAN.getApiKey3(), WX_PAY_V3_BEAN.getPlatformCertPath()); + // 微信公钥验证签名并解密 +// if (ObjectUtil.equals(serialNo, WX_PAY_V3_BEAN.getPublicKeyId())) { +// plainText = WxPayKit.verifyPublicKeyNotify(result, signature, nonce, timestamp, +// WX_PAY_V3_BEAN.getApiKey3(), WX_PAY_V3_BEAN.getPlatformCertPath()); +// } + log.info("支付通知明文 {}", plainText); + if (StrUtil.isNotEmpty(plainText)) { + // 支付结果 + JSONObject jsonResult = JSONUtil.parseObj(plainText); + // 获取缴费记录,并修改缴费结果 + PaymentTransaction transaction = sysUserService.dao().fetch(PaymentTransaction.class, Cnd.where("outTradeNo", "=", jsonResult.getStr("out_trade_no"))); + transaction.setTradingState(TradingStateEnum.PAY_SUCCESS.getTrdingStateName()); + transaction.setTransactionId(jsonResult.getStr("transaction_id")); + sysUserService.dao().update(transaction); + + sysUserService.dao().update(MemberPaymentInfo.class, + Chain.make("isPayment", true).add("paymentTime", DateUtil.date(jsonResult.getLong("success_time"))) + .add("paymentMoney", String.valueOf(transaction.getTransactionId())), + Cnd.where("payId", "=", transaction.getId()) + ); + + response.setStatus(200); + map.put("code", "SUCCESS"); + map.put("message", "SUCCESS"); + } else { + response.setStatus(500); + map.put("code", "ERROR"); + map.put("message", "签名错误"); + log.error("签名错误"); + } + + // 支付回调,返回给微信,自己也接收不到结果,这里的返回没用 + response.setHeader("Content-type", ContentType.JSON.toString()); + response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8)); + response.flushBuffer(); + } catch (Exception e) { + log.error("系统异常", e); + } + } + + + /** + * 退款 + * @param transactionId 微信订单号 + * @param outTradeNo 商户订单号 + * @return + */ + @Override + public Result refund(String transactionId, String outTradeNo) { + try { + // 查询商户订单 + PaymentTransaction transaction = sysUserService.dao().fetch(PaymentTransaction.class, Cnd.where("outTradeNo", "=", outTradeNo)); + Integer tradingAmount = transaction.getTradingAmount(); + String outRefundNo = PayKit.generateStr(); + log.info("商户退款单号: {}", outRefundNo); + + // 没用 +// List list = new ArrayList<>(); +// RefundGoodsDetail refundGoodsDetail = new RefundGoodsDetail() +// .setMerchant_goods_id("123") +// .setGoods_name("测试") +// .setUnit_price(1) +// .setRefund_amount(1) +// .setRefund_quantity(1); +// list.add(refundGoodsDetail); + + RefundModel refundModel = new RefundModel() + .setOut_refund_no(outRefundNo) + .setReason(transaction.getDescription() + "退款") + .setNotify_url(refundNotifyUrl) + .setAmount(new RefundAmount().setRefund(tradingAmount).setTotal(tradingAmount).setCurrency("CNY")); +// .setGoods_detail(list); + + if (StrUtil.isNotEmpty(transactionId)) { + refundModel.setTransaction_id(transactionId); + } + if (StrUtil.isNotEmpty(outTradeNo)) { + refundModel.setOut_trade_no(outTradeNo); + } + log.info("退款参数 {}", JSONUtil.toJsonStr(refundModel)); + IJPayHttpResponse response = WxPayApi.v3( + RequestMethodEnum.POST, + WxDomainEnum.CHINA.toString(), + BasePayApiEnum.REFUND.toString(), + WX_PAY_V3_BEAN.getMchId(), + getSerialNumber(), + null, + WX_PAY_V3_BEAN.getKeyPath(), + JSONUtil.toJsonStr(refundModel) + ); + // 根据证书序列号查询对应的证书来验证签名结果 + boolean verifySignature = WxPayKit.verifySignature(response, WX_PAY_V3_BEAN.getPlatformCertPath()); + // 微信支付公钥验证签名 +// boolean verifySignature = WxPayKit.verifyPublicKeySignature(response, WX_PAY_V3_BEAN.getPlatformCertPath()); + log.info("verifySignature: {}", verifySignature); + log.info("退款响应 {}", response); + + if (verifySignature) { + // 用户发起退款,存一个退款记录 + PaymentRefundRecord record = new PaymentRefundRecord(); + record.setId(R.UU32()); + record.setPaymentTransactionId(transaction.getId()); + record.setOutRefundTradeNo(outRefundNo); + record.setRefundNo(null); + record.setRefundState(RefundStateEnum.REFUNDING.name()); + record.setRefundAmount(tradingAmount); + record.setTotal(tradingAmount); + record.setResultJson(response.getBody()); + sysUserService.dao().insert(record); + + // 返回退款下单结果 + return Result.success().addData(response.getBody()); + } + } catch (Exception e) { + log.error("系统异常", e); + return Result.error(e.getMessage()); + } + return null; + } + + + /** + * 退款回调 + * @param request + * @param response + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public void refundNotify(HttpServletRequest request, HttpServletResponse response) { + Map map = new HashMap<>(12); + try { + String timestamp = request.getHeader("Wechatpay-Timestamp"); + String nonce = request.getHeader("Wechatpay-Nonce"); + String serialNo = request.getHeader("Wechatpay-Serial"); + String signature = request.getHeader("Wechatpay-Signature"); + + log.info("退款通知 timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature); + String result = HttpKit.readData(request); + log.info("退款通知密文 {}", result); +// String plainText = null; + // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号 + String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, + WX_PAY_V3_BEAN.getApiKey3(), WX_PAY_V3_BEAN.getPlatformCertPath()); + // 微信公钥验证签名并解密 +// if (ObjectUtil.equals(serialNo, WX_PAY_V3_BEAN.getPublicKeyId())) { +// plainText = WxPayKit.verifyPublicKeyNotify(result, signature, nonce, timestamp, +// WX_PAY_V3_BEAN.getApiKey3(), WX_PAY_V3_BEAN.getPlatformCertPath()); +// } + log.info("退款通知明文 {}", plainText); + + if (StrUtil.isNotEmpty(plainText)) { + // 取出明文,修改退款的结果 + JSONObject jsonObject = JSONUtil.parseObj(plainText); + // 查询出退款记录,存储关键信息并修改 + PaymentRefundRecord record = dao.fetch(PaymentRefundRecord.class, Cnd.where("outRefundTradeNo", "=", jsonObject.getStr("out_refund_no"))); + record.setRefundNo(jsonObject.getStr("refund_id")); + record.setRefundState(RefundStateEnum.REFUND_SUCCESS.name()); + // 到此是退款成功了,还要去修改订单表的数据 + dao.update(PaymentTransaction.class, Chain.make("isRefund", true) + .add("refundAmount", record.getRefundAmount()), + Cnd.where("id", "=", record.getPaymentTransactionId())); + + response.setStatus(200); + map.put("code", "SUCCESS"); + map.put("message", "SUCCESS"); + } else { + response.setStatus(500); + map.put("code", "ERROR"); + map.put("message", "签名错误"); + } + // 没用 + response.setHeader("Content-type", ContentType.JSON.toString()); + response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8)); + response.flushBuffer(); + } catch (Exception e) { + log.error("系统异常", e); + } + } + + + /** + * 公共方法,获取证书序列号 + * @return + */ + private String getSerialNumber() { + if (StrUtil.isEmpty(serialNo)) { + // 获取证书序列号 + X509Certificate certificate = PayKit.getCertificate(WX_PAY_V3_BEAN.getCertPath()); + if (null != certificate) { + serialNo = certificate.getSerialNumber().toString(16).toUpperCase(); + // 提前两天检查证书是否有效 + boolean isValid = PayKit.checkCertificateIsValid(certificate, WX_PAY_V3_BEAN.getMchId(), -2); + log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN)); + } +// System.out.println("输出证书信息:\n" + certificate.toString()); +// // 输出关键信息,截取部分并进行标记 +// System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16)); +// System.out.println("版本号:" + certificate.getVersion()); +// System.out.println("签发者:" + certificate.getIssuerDN()); +// System.out.println("有效起始日期:" + certificate.getNotBefore()); +// System.out.println("有效终止日期:" + certificate.getNotAfter()); +// System.out.println("主体名:" + certificate.getSubjectDN()); +// System.out.println("签名算法:" + certificate.getSigAlgName()); +// System.out.println("签名:" + certificate.getSignature().toString()); + } + System.out.println("serialNo:" + serialNo); + return serialNo; + } + + + /** + * 公共方法,获取平台证书序列号 + * @return + */ + private String getPlatSerialNumber() { + if (StrUtil.isEmpty(platSerialNo)) { + // 获取平台证书序列号 + X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(WX_PAY_V3_BEAN.getPlatformCertPath())); + platSerialNo = certificate.getSerialNumber().toString(16).toUpperCase(); + } + System.out.println("platSerialNo:" + platSerialNo); + return platSerialNo; + } + + + /** + * 公共方法,保存平台证书 + * @param associatedData + * @param nonce + * @param cipherText + * @param algorithm + * @param certPath + * @return + */ + private String savePlatformCert(String associatedData, String nonce, String cipherText, String algorithm, String certPath) { + try { + String key3 = WX_PAY_V3_BEAN.getApiKey3(); + String publicKey; + if (StrUtil.equals(algorithm, AuthTypeEnum.SM2.getPlatformCertAlgorithm())) { + publicKey = PayKit.sm4DecryptToString(key3, cipherText, nonce, associatedData); + } else { + AesUtil aesUtil = new AesUtil(WX_PAY_V3_BEAN.getApiKey3().getBytes(StandardCharsets.UTF_8)); + // 平台证书密文解密 + // encrypt_certificate 中的 associated_data nonce ciphertext + publicKey = aesUtil.decryptToString( + associatedData.getBytes(StandardCharsets.UTF_8), + nonce.getBytes(StandardCharsets.UTF_8), + cipherText + ); + } + if (StrUtil.isNotEmpty(publicKey)) { + // 保存证书 + FileWriter writer = new FileWriter(certPath); + writer.write(publicKey); + // 获取平台证书序列号 + X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes())); + return certificate.getSerialNumber().toString(16).toUpperCase(); + } + return ""; + } catch (Exception e) { + log.error("保存平台证书异常", e); + return e.getMessage(); + } + } + + + /** + * 公共方法,构建订单关闭时间,默认3分钟 + * @return + */ + private String getNowTimeExpire() { + long currentTimeMillis = System.currentTimeMillis() + 1000 * 60 * 3; + // 将时间戳转换为 DateTime 对象 + DateTime dateTime = DateUtil.date(currentTimeMillis); + return DateUtil.format(dateTime, "yyyy-MM-dd'T'HH:mm:ssXXX"); + } + + + /** + * 公共方法,插入交易订单 + * @param paramVo 交易必填参数Vo + * @param outTradeNo 商户交易号 + * @param result 下单返回结构 + */ + private void insertTradingOrder(WxPayNeedParamVo paramVo, String outTradeNo, String result){ + User user = sysUserService.dao().fetch(User.class, Cnd.where("id", "=", ShiroUtil.getUserId())); + PaymentTransaction transaction = new PaymentTransaction(); + transaction.setId(R.UU32()); + transaction.setMchId(WX_PAY_V3_BEAN.getMchId()); + transaction.setOutTradeNo(outTradeNo); + transaction.setTransactionId(null); + transaction.setTradingChannel(TradingChannelEnum.WX_PAY.name()); + transaction.setTradingType(TradingTypeEnum.PAYMENT.name()); + transaction.setUserId(user.getId()); + transaction.setLoginName(user.getLoginname()); + transaction.setUserName(user.getUsername()); + transaction.setUnitId(user.getUnitid()); + transaction.setUnitName(user.getUnitname()); + transaction.setUnionId(user.getUnionid()); + transaction.setUnionName(user.getUnionname()); + transaction.setTradingState(TradingStateEnum.WAITING_FOR_PAYMENT.name()); + // 获取订单金额 + transaction.setTradingAmount(paramVo.getAmount().getTotal()); + transaction.setIsRefund(false); + transaction.setRefundAmount(null); + transaction.setDescription(paramVo.getDescription()); + transaction.setAttach(paramVo.getAttach()); + transaction.setOpenId(null); + transaction.setResultJson(result); + transaction.setMemberPaymentInfoId(paramVo.getMemberPaymentInfoId()); + sysUserService.dao().insert(transaction); + } +} diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/util/WeChatUtils.java b/src/main/java/io/v/nutz/base/payment/wxpay/util/WeChatUtils.java new file mode 100644 index 0000000..e1dd698 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/util/WeChatUtils.java @@ -0,0 +1,109 @@ +package io.v.nutz.base.payment.wxpay.util; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import io.v.nutz.base.constant.RedisConstant; +import lombok.extern.slf4j.Slf4j; +import org.nutz.http.Http; +import org.nutz.integration.jedis.RedisService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.util.NutMap; + +import java.net.URLEncoder; + +/** + * @version 1.0 + * @Author zzr + * @name:WeChatUtils + * @Date 2025/3/10 14:13 + * @注释 + */ +@Slf4j +@IocBean +public class WeChatUtils { + + @Inject + private RedisService redisService; + + public static final String APPID = "wxf12fa6175ec7c75f"; + + public static final String SECRET = "f65bbb41d3de4a93c8c93d8e2fd50369"; + + // 获取code + public final static String CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"; + + //获取网页access_token的接口地址 + public final static String WEB_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code"; + + + //获取用户信息地址 + public final static String USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN"; + + // 获取access_token的接口地址(GET) 限2000(次/天) + public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; + // 发送模板消息 + public static final String SEND_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN"; + + + /** + * 生成获取code的url + * + * @param redirectUrl + * @return + */ + public String getCodeUrl(String redirectUrl, String state) { + String requestUrl = null; + state = state == null ? "" : state; + try { + requestUrl = CODE_URL.replace("APPID", APPID).replace("REDIRECT_URI", URLEncoder.encode(redirectUrl, "UTF-8")).replace("STATE", state); + } catch (Exception e) { + e.printStackTrace(); + } + return requestUrl; + } + + /** + * 获取微信网页access_token 和 openid + * + * @param code + * @return + */ + public JSONObject getWebAccessTokenAndOpenid(String code) { + String requestUrl = WEB_ACCESS_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET).replace("CODE", code); + return JSONUtil.parseObj(HttpUtil.post(requestUrl, new JSONObject())); + } + + /** + * 网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了 + * ... + * + * @param webAccessToken + * @param openid + * @return + */ + public JSONObject getUserInfo(String webAccessToken, String openid) { + String requestUrl = USERINFO_URL.replace("ACCESS_TOKEN", webAccessToken).replace("OPENID", openid); + return JSONUtil.parseObj(HttpUtil.post(requestUrl, new JSONObject())); + } + + /** + * 获取access_token 先从Redis获取,如果没有再调微信获取并存Redis + * + * @return + */ + public String getAccessToken() { + String token = redisService.get(RedisConstant.REDIS_KEY_WECHAT_ACCESS_TOKEN); + if (StrUtil.isNotBlank(token)) { + return token; + } + String accessTokenUrl = ACCESS_TOKEN_URL.formatted(APPID, SECRET); + String accessToken = Json.fromJson(NutMap.class, Http.get(accessTokenUrl).getContent()).getString("access_token"); + redisService.setex(RedisConstant.REDIS_KEY_WECHAT_ACCESS_TOKEN, 60 * 60 * 2, accessToken); + return accessToken; + } + +} diff --git a/src/main/java/io/v/nutz/base/payment/wxpay/vo/WxPayNeedParamVo.java b/src/main/java/io/v/nutz/base/payment/wxpay/vo/WxPayNeedParamVo.java new file mode 100644 index 0000000..c035812 --- /dev/null +++ b/src/main/java/io/v/nutz/base/payment/wxpay/vo/WxPayNeedParamVo.java @@ -0,0 +1,36 @@ +package io.v.nutz.base.payment.wxpay.vo; + +import com.ijpay.wxpay.model.v3.Amount; +import lombok.Data; + +/** + * @version 1.0 + * @Author zzr + * @name:WxPayNeedParamVo + * @Date 2025/3/7 18:43 + * @注释 + */ +@Data +public class WxPayNeedParamVo { + + /** + * 订单描述,会显示在支付界面的标题,最大长度127,必填 + */ + private String description; + + /** + * 附加数据,订单的详细描述信息,不必填 + */ + private String attach; + + /** + * 金额对象,对象中的 total 是金额,必填; + * 其余参数可不传递也可不填 + */ + private Amount amount; + + /** + * 会员支付信息ID,必填 + */ + private String memberPaymentInfoId; +} diff --git a/src/main/java/io/v/nutz/base/utils/WebUtil.java b/src/main/java/io/v/nutz/base/utils/WebUtil.java new file mode 100644 index 0000000..7bd68e7 --- /dev/null +++ b/src/main/java/io/v/nutz/base/utils/WebUtil.java @@ -0,0 +1,33 @@ +package io.v.nutz.base.utils; + +import org.nutz.json.JsonFormat; +import org.nutz.lang.Encoding; +import org.nutz.mvc.view.UTF8JsonView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author wizzer@qq.com + */ +public class WebUtil { + + public static String AjaxEncode = Encoding.UTF8; + + public static boolean isAjax(HttpServletRequest req) { + String value = req.getHeader("X-Requested-With"); + return value != null && "XMLHttpRequest".equalsIgnoreCase(value.trim()); + } + + public static void rendAjaxResp(HttpServletRequest req, HttpServletResponse resp, Object re) { + try { + if (AjaxEncode != null) { + resp.setCharacterEncoding(AjaxEncode); + } + (new UTF8JsonView(JsonFormat.compact())).render(req, resp, re); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/io/v/nutz/sys/controllers/platform/sys/SysWechatController.java b/src/main/java/io/v/nutz/sys/controllers/platform/sys/SysWechatController.java index 6b7ca00..81e217b 100644 --- a/src/main/java/io/v/nutz/sys/controllers/platform/sys/SysWechatController.java +++ b/src/main/java/io/v/nutz/sys/controllers/platform/sys/SysWechatController.java @@ -1,6 +1,37 @@ package io.v.nutz.sys.controllers.platform.sys; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.socket.SocketUtil; +import io.v.nutz.base.payment.wxpay.util.WeChatUtils; +import io.v.nutz.base.utils.LoginUtil; +import io.v.nutz.sys.models.Sys_user; +import io.v.nutz.sys.services.SysUserService; +import io.v.nutz.web.commons.base.Globals; +import io.v.nutz.web.commons.slog.SLogService; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.LockedAccountException; +import org.apache.shiro.authc.UnknownAccountException; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.integration.jedis.RedisService; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Attr; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.net.InetAddress; +import java.net.Socket; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Optional; /** * 微信公众号 @@ -9,5 +40,167 @@ import org.nutz.ioc.loader.annotation.IocBean; * @date 2023/07/06 */ @IocBean +@At("/platform/wechat") public class SysWechatController { + + private static String ACCESS_TOKEN_URL = "https://%s/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"; + + private static final String APPID = "wxf12fa6175ec7c75f"; + + private static final String SECRET = "f65bbb41d3de4a93c8c93d8e2fd50369"; + + + @Inject + private Dao dao; + @Inject + private WeChatUtils weChatUtils; + @Inject + private SysUserService sysUserService; + @Inject + private RedisService redisService; + @Inject + private SLogService sLogService; + @Inject + private LoginUtil loginUtil; + + /** + * oauth + * + * @return {@link String} + */ + @At + @Ok("re") + public String oauth(@Param("redirectUrl") String redirectUrl) { + String redirect = Globals.AppDomain + "/platform/wechat/getCode"; + if (StrUtil.isNotBlank(redirect)) { + redirect += "?redirect=" + redirect; + } + String codeUrl = weChatUtils.getCodeUrl(redirect, ""); + return "redirect:" + codeUrl; + } + + /** + * 获取code 登录用户 + * + * @param code 代码 + * @return {@link String} + */ + @At + @Ok("re") + public String getCode(@Param("code") String code, @Param("redirect") String redirect, HttpSession session, HttpServletRequest request) { + String host; + try { + Socket socket = SocketUtil.connect("api.weixin.qq.com", 443, 1000); + if (socket.isConnected()) { + host = "api.weixin.qq.com"; + } else { + InetAddress inetAddress = InetAddress.getLocalHost(); + host = inetAddress.getHostAddress(); + } + } catch (Exception e) { + host = "api.weixin.qq.com"; + } + String tokenUrl = ACCESS_TOKEN_URL.formatted(host, APPID, SECRET, code); + + NutMap resMap = Json.fromJson(NutMap.class, HttpUtil.get(tokenUrl)); + String openid = resMap.getString("openid"); + Sys_user sysUser = dao.fetch(Sys_user.class, Cnd.where("openId", "=", openid)); + + if (sysUser == null) { + String s = Globals.AppDomain + "/platform/wechat/doLogin?openid=" + openid; + if (StrUtil.isNotBlank(redirect)) { + s += "&redirect=" + URLEncoder.encode(redirect, StandardCharsets.UTF_8); + } +// return ">>:" + Globals.CasAddress + "/login?service=" + URLEncoder.encode(s, StandardCharsets.UTF_8); + return ">>:" + s; + } + + String loginName = Optional.ofNullable(sysUser).map(x -> x.getLoginname()).orElse(null); + + try { + loginUtil.doLogin(loginName, request, session, LoginUtil.LoginOrigin.WEI_XIN); + if (StrUtil.isNotBlank(redirect)) { + return ">>:" + redirect; + } + return ">>:" + Globals.AppDomain + "/platform/home"; + } catch (LockedAccountException e) { + request.setAttribute("errMsg", "帐号被锁定,请5分钟后再试"); + return ">>:" + Globals.AppDomain + "/platform/home/LockedAccountError"; + } catch (UnknownAccountException e) { + request.setAttribute("errMsg", "系统中没有此用户"); + return ">>:" + Globals.AppDomain + "/error/UnknownAccountError.html"; + } catch (AuthenticationException e) { + request.setAttribute("errMsg", "系统中没有此用户"); + return ">>:" + Globals.AppDomain + "/error/UnknownAccountError.html"; + } catch (Exception e) { + request.setAttribute("errMsg", "用户名或密码错误"); + return ">>:" + Globals.AppDomain + "/error/500.html"; + } + } + + + @At + @Ok("re") + public Object doLogin(@Attr("platformLoginToken") AuthenticationToken token, + HttpServletRequest request, HttpSession session, + @Param("redirect") String redirect, @Param("openid") String openid) { +// int errCount = NumberUtils.toInt(Strings.sNull(SecurityUtils.getSubject().getSession(true).getAttribute("platformErrCount"))); +// try { +// loginUtil.doLogin(token, request, session, LoginUtil.LoginOrigin.WEI_XIN); +// +// return Result.success("login.success"); +// } catch (CaptchaIncorrectException e) { +// return Result.error(1, "login.error.captcha"); +// } catch (CaptchaEmptyException e) { +// return Result.error(2, "login.error.captcha"); +// } catch (LockedAccountException e) { +// return Result.error(3, "login.error.locked"); +// } catch (UnknownAccountException e) { +// errCount++; +// SecurityUtils.getSubject().getSession(true).setAttribute("platformErrCount", errCount); +// return Result.error(4, "login.error.user"); +// } catch (AuthenticationException e) { +// errCount++; +// SecurityUtils.getSubject().getSession(true).setAttribute("platformErrCount", errCount); +// return Result.error(5, "login.error.user"); +// } catch (Exception e) { +// errCount++; +// SecurityUtils.getSubject().getSession(true).setAttribute("platformErrCount", errCount); +// return Result.error(6, "login.error.system"); +// } + + +// Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); +// if (assertion == null) { +// return ">>:" + Globals.AppDomain + "/platform/home/UnknownAccountError"; +// } +// +// String principal = assertion.getPrincipal().toString(); +// //工号 手机号 身份证 +// Sys_user user = dao.fetch(Sys_user.class, Cnd.NEW().and("loginname", "=", principal).or("mobile", "=", principal).or("idcard", "=", principal)); +// String loginName = Optional.ofNullable(user).map(v -> v.getLoginname()).orElse(null); +// +// try { +// loginUtil.doLogin(loginName, request, session, LoginUtil.LoginOrigin.WEI_XIN); +// //绑定openId +// if (StrUtil.isNotBlank(openid)) { +// dao.update(Sys_user.class, Chain.make("openId", openid), Cnd.where("loginName", "=", loginName)); +// } +// if (StrUtil.isNotBlank(redirect)) { +// return ">>:" + redirect; +// } +// return ">>:" + Globals.AppDomain + "/mobile/index"; +// } catch (LockedAccountException e) { +// request.setAttribute("errMsg", "帐号被锁定,请5分钟后再试"); +// return ">>:" + Globals.AppDomain + "/platform/home/LockedAccountError"; +// } catch (UnknownAccountException e) { +// request.setAttribute("errMsg", "系统中没有此用户"); +// return ">>:" + Globals.AppDomain + "/platform/home/UnknownAccountError"; +// } catch (Exception e) { +// request.setAttribute("errMsg", "用户名或密码错误"); +// return ">>:" + Globals.AppDomain + "/platform/home/500"; +// } + return null; + } + } diff --git a/src/main/java/io/v/nutz/sys/models/Sys_user.java b/src/main/java/io/v/nutz/sys/models/Sys_user.java index a38395e..3fbe0b4 100644 --- a/src/main/java/io/v/nutz/sys/models/Sys_user.java +++ b/src/main/java/io/v/nutz/sys/models/Sys_user.java @@ -626,6 +626,11 @@ public class Sys_user extends BaseModel implements Serializable { @ColDefine(type = ColType.VARCHAR, width = 300) private String weAppOpenid; + @Column + @Comment("微信openId") + @ColDefine(type = ColType.VARCHAR, width = 300) + private String openId; + /** * 2021-05-18 end */ diff --git a/src/main/java/io/v/nutz/web/commons/proc/WkFailProcessor.java b/src/main/java/io/v/nutz/web/commons/proc/WkFailProcessor.java index 34e5cd9..5707521 100644 --- a/src/main/java/io/v/nutz/web/commons/proc/WkFailProcessor.java +++ b/src/main/java/io/v/nutz/web/commons/proc/WkFailProcessor.java @@ -1,6 +1,8 @@ package io.v.nutz.web.commons.proc; +import io.v.nutz.base.exception.BaseException; import io.v.nutz.base.result.Result; +import io.v.nutz.base.utils.WebUtil; import org.nutz.integration.shiro.NutShiro; import org.nutz.ioc.IocException; import org.nutz.log.Log; @@ -27,13 +29,24 @@ public class WkFailProcessor extends ViewProcessor { String uri = Mvcs.getRequestPath(ac.getRequest()); log.warn(String.format("Error@%s :", uri), ac.getError()); } - if (ac.getError() instanceof IocException) { + Throwable e = ac.getError(); + if (e instanceof IocException) { if (NutShiro.isAjax(ac.getRequest())) { NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error(Mvcs.getMessage(ac.getRequest(), "system.exception"))); } else { ac.getRequest().setAttribute("original_request_uri",ac.getRequest().getRequestURI()); new ForwardView(errorUri).render(ac.getRequest(), ac.getResponse(), Mvcs.getMessage(ac.getRequest(), "system.exception")); } + } else if (e instanceof BaseException) { + if (WebUtil.isAjax(ac.getRequest())) { +// WebUtil.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error(ResultCode.FAILURE.getCode(), !log.isDebugEnabled() ? ResultCode.FAILURE.getMsg() : e.getMessage())); + WebUtil.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error(400, e.getMessage())); + } else { + ac.getRequest().setAttribute("original_request_uri", ac.getRequest().getRequestURI()); + ac.getRequest().setAttribute("error_message", Mvcs.getMessage(ac.getRequest(), "业务异常")); +// new ForwardView(errorUri).render(ac.getRequest(), ac.getResponse(), null); + } + return; } super.process(ac); } diff --git a/src/main/java/io/v/nutz/web/commons/shiro/config/EasyCredentialsMatch.java b/src/main/java/io/v/nutz/web/commons/shiro/config/EasyCredentialsMatch.java index aa81ce4..fe13696 100644 --- a/src/main/java/io/v/nutz/web/commons/shiro/config/EasyCredentialsMatch.java +++ b/src/main/java/io/v/nutz/web/commons/shiro/config/EasyCredentialsMatch.java @@ -7,6 +7,7 @@ import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; + import java.util.Arrays; public class EasyCredentialsMatch extends HashedCredentialsMatcher { @@ -25,7 +26,7 @@ public class EasyCredentialsMatch extends HashedCredentialsMatcher { LoginUtil.LoginOrigin loginOrigin = platformCaptchaToken.getLoginOrigin(); //cas和企业微信实现免密登录 - if (loginOrigin.equals(LoginUtil.LoginOrigin.CAS) || loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN)) { + if (loginOrigin.equals(LoginUtil.LoginOrigin.CAS) || loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN) || !loginOrigin.equals(LoginUtil.LoginOrigin.WEI_XIN)) { return true; } diff --git a/src/main/java/io/v/nutz/web/commons/shiro/realm/PlatformAuthorizingRealm.java b/src/main/java/io/v/nutz/web/commons/shiro/realm/PlatformAuthorizingRealm.java index e97c3af..cc1effe 100644 --- a/src/main/java/io/v/nutz/web/commons/shiro/realm/PlatformAuthorizingRealm.java +++ b/src/main/java/io/v/nutz/web/commons/shiro/realm/PlatformAuthorizingRealm.java @@ -94,7 +94,7 @@ public class PlatformAuthorizingRealm extends AuthorizingRealm { throw Lang.makeThrow(AuthenticationException.class, "验证码失效,请重新获取"); } user = sysUserService.fetch(Cnd.NEW().and("mobile", "=", mobile)); - }else if (!loginOrigin.equals(LoginUtil.LoginOrigin.CAS) && !loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN)) { + }else if (!loginOrigin.equals(LoginUtil.LoginOrigin.CAS) && !loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN) && !loginOrigin.equals(LoginUtil.LoginOrigin.WEI_XIN)) { validateService.checkCode(key, captcha); if (Strings.isBlank(loginname)) { throw Lang.makeThrow(AuthenticationException.class, "登录账号不可为空"); @@ -116,7 +116,7 @@ public class PlatformAuthorizingRealm extends AuthorizingRealm { throw Lang.makeThrow(LockedAccountException.class, "账号被禁用", loginname); } - if (!loginOrigin.equals(LoginUtil.LoginOrigin.CAS) && !loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN)) { + if (!loginOrigin.equals(LoginUtil.LoginOrigin.CAS) && !loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN) && !loginOrigin.equals(LoginUtil.LoginOrigin.WEI_XIN)) { int errCount = NumberUtils.toInt(Strings.sNull(session.getAttribute("platformErrCount"))); if (errCount >= 4) { //输错5次禁用账户10分钟 diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentChartSummaryController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentChartSummaryController.java new file mode 100644 index 0000000..51ef648 --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentChartSummaryController.java @@ -0,0 +1,180 @@ +package io.v.nutz.zhgh.memberCost.controller; + +import cn.wizzer.framework.base.Result; +import io.v.nutz.zhgh.memberCost.model.MemberPaymentProject; +import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Cnd; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.Static; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @ClassName MemberPaymentChartSummaryController + * @Description 图表汇总 + * @Author zhf + * @Date 2025/3/11 下午3:20 + */ +@IocBean +@Slf4j +@Ok("json:full") +@At("/platform/member/payment/chartSummary") +public class MemberPaymentChartSummaryController { + + + @Inject + private MemberPaymentService memberPaymentService; + + + @At("") + @Ok("beetl:/platform/membercost/chartSummary/index.html") + @RequiresPermissions("member.payment.chartSummary") + public void index() { + } + + + @At + @RequiresPermissions("member.payment.chartSummary") + public Object getPayProjectChart(@Param(value = "startYear", required = false) Integer startYear, + @Param(value = "endYear", required = false) Integer endYear) { + Sql sql = Sqls.create(""" + SELECT + mpp.projectName, + COUNT( mpi.id ) AS num + FROM + member_payment_project mpp + LEFT JOIN member_payment_info mpi ON mpi.paymentProjectId = mpp.id + AND mpi.isPayment = 1 + WHERE + YEAR ( mpp.createTime ) BETWEEN @startYear + AND @endYear + GROUP BY + mpp.id + """).setParam("startYear", startYear).setParam("endYear", endYear); + return Result.success(memberPaymentService.listMap(sql)); + } + + @At + @RequiresPermissions("member.payment.chartSummary") + public Object getRefundProjectChart(@Param(value = "startYear", required = false) Integer startYear, + @Param(value = "endYear", required = false) Integer endYear) { + Sql sql = Sqls.create(""" + SELECT + mpp.projectName, + COUNT( pt.id ) AS num + FROM + member_payment_project mpp + LEFT JOIN member_payment_info mpi ON mpi.paymentProjectId = mpp.id + LEFT JOIN payment_transaction pt ON pt.memberPaymentInfoId = mpi.id + AND pt.isRefund = 1 + WHERE + YEAR ( mpp.createTime ) BETWEEN @startYear + AND @endYear + GROUP BY + mpp.id + """).setParam("startYear", startYear).setParam("endYear", endYear); + return Result.success(memberPaymentService.listMap(sql)); + } + + @At + @RequiresPermissions("member.payment.chartSummary") + public Object getPayMoneyChart(@Param(value = "startYear", required = false) Integer startYear, + @Param(value = "endYear", required = false) Integer endYear) { + Sql sql = Sqls.create(""" + SELECT + mpp.projectName, + SUM( pt.tradingAmount * 0.01 ) AS money + FROM + member_payment_project mpp + LEFT JOIN member_payment_info mpi ON mpi.paymentProjectId = mpp.id + AND mpi.isPayment = 1 + LEFT JOIN payment_transaction pt ON pt.memberPaymentInfoId = mpi.id + AND pt.isRefund = 0 + AND pt.tradingState = 'PAY_SUCCESS' + WHERE + YEAR ( mpp.createTime ) BETWEEN @startYear + AND @endYear + GROUP BY + mpp.id + """).setParam("startYear", startYear).setParam("endYear", endYear); + return Result.success(memberPaymentService.listMap(sql)); + } + + + @At + @RequiresPermissions("member.payment.chartSummary") + public Object getNumData(@Param(value = "startYear", required = false) Integer startYear, + @Param(value = "endYear", required = false) Integer endYear) { + + Sql sql = Sqls.create(""" + SELECT + pt.tradingAmount * 0.01 AS money, + pt.isRefund + FROM + member_payment_project mpp + LEFT JOIN member_payment_info mpi ON mpi.paymentProjectId = mpp.id + AND mpi.isPayment = 1 + LEFT JOIN payment_transaction pt ON pt.memberPaymentInfoId = mpi.id + AND pt.tradingState = 'PAY_SUCCESS' + WHERE + YEAR ( mpp.createTime ) BETWEEN @startYear + AND @endYear + """).setParam("startYear", startYear).setParam("endYear", endYear); + List map = memberPaymentService.listMap(sql); + + double payMoney = map.stream().filter(v -> !v.getBoolean("isRefund")).mapToDouble(v -> v.getDouble("money")).sum(); + double refundMoney = map.stream().filter(v -> v.getBoolean("isRefund")).mapToDouble(v -> v.getDouble("money")).sum(); + int refundSize = map.stream().filter(v -> v.getBoolean("isRefund")).collect(Collectors.toList()).size(); + int paySize = map.stream().filter(v -> !v.getBoolean("isRefund")).collect(Collectors.toList()).size(); + NutMap data = new NutMap(); + data.put("payMoney", payMoney); + data.put("refundMoney", refundMoney); + data.put("paySize", paySize); + data.put("refundSize", refundSize); + return Result.success(data); + } + + + @At + @RequiresPermissions("member.payment.chartSummary") + public Object allUnionMoney(String paymentProjectId) { + Sql sql = Sqls.create(""" + SELECT + un.unionname, + COALESCE ( SUM( subquery.money ), 0 ) AS money + FROM + sys_union un + LEFT JOIN ( + SELECT + pt.unionId, + SUM( pt.tradingAmount * 0.01 ) AS money + FROM + payment_transaction pt + INNER JOIN member_payment_info mpi ON mpi.id = pt.memberPaymentInfoId + AND mpi.paymentProjectId = @paymentProjectId + WHERE + pt.tradingState = 'PAY_SUCCESS' + AND pt.isRefund = 0 + GROUP BY + pt.unionId + ) subquery ON subquery.unionId = un.id + GROUP BY + un.id, + un.unionname, + un.unioncode + ORDER BY + un.unioncode; + """).setParam("paymentProjectId", paymentProjectId); + return Result.success(memberPaymentService.listMap(sql)); + } +} diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentConfigController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentConfigController.java new file mode 100644 index 0000000..6b11307 --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentConfigController.java @@ -0,0 +1,92 @@ +package io.v.nutz.zhgh.memberCost.controller; + +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.dao.CndPlus; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.zhgh.memberCost.model.MemberPaymentConfig; +import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Strings; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + + +@IocBean +@Slf4j +@Ok("json:full") +@At("/platform/member/payment/config") +public class MemberPaymentConfigController { + + @Inject + private MemberPaymentService memberPaymentService; + + @At("") + @Ok("beetl:/platform/membercost/config.html") + @RequiresPermissions("member.payment.config") + public void index() {} + + + @At + @ViReturn + @RequiresPermissions("member.payment.config") + public Object pageData(PageForm pageForm, + @Param(value = "name", required = false) String name) { + Sql sql = Sqls.create("select * from member_payment_config $condition"); + CndPlus cnd = CndPlus.create(); + + if (Strings.isNotBlank(name)) { + cnd.and(Cnd.likeEX("name", name)); + } +// cnd.desc("project.`year`"); + cnd.desc("createdAt"); + sql.setCondition(cnd); + return memberPaymentService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + } + + @At + @ViReturn + public Object getPaymentConfig(){ + return memberPaymentService.getConfig(); + } + + @At + @ViReturn + public Object getPaymentConfigList(){ + return memberPaymentService.getPaymentConfigList(); + } + + @At + @ViReturn + public Object doRemove(String id){ + memberPaymentService.dao().clear(MemberPaymentConfig.class, Cnd.where("id", "=", id)); + return null; + } + + @At + @ViReturn + public Object openClosedConfig(String id){ + //位运算 取反 + memberPaymentService.dao().update(MemberPaymentConfig.class, + Chain.makeSpecial("isDisabled", "isDisabled ^ 1"), + Cnd.where("id", "=", id)); + return null; + } + + + @At + @ViReturn + @RequiresPermissions("member.payment.config") + public Object doAddOrModify(MemberPaymentConfig memberPaymentConfig){ + System.out.println(memberPaymentConfig); + memberPaymentService.dao().insertOrUpdate(memberPaymentConfig); + return null; + } +} diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentMineController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentMineController.java new file mode 100644 index 0000000..3626de7 --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentMineController.java @@ -0,0 +1,106 @@ +package io.v.nutz.zhgh.memberCost.controller; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.wizzer.framework.base.Result; +import com.ijpay.wxpay.model.v3.Amount; +import io.v.nutz.base.annontation.RepeatSubmit; +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.payment.wxpay.service.WxPayCommonService; +import io.v.nutz.base.payment.wxpay.vo.WxPayNeedParamVo; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.web.commons.utils.ShiroUtil; +import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo; +import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Cnd; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +/** + * @version 1.0 + * @Author zzr + * @name:MemberPaymentMineController + * @Date 2025/3/10 8:44 + * @注释 我的缴费列表 + */ +@Slf4j +@IocBean +@Ok("json:full") +@At("/platform/member/payment/mine") +public class MemberPaymentMineController { + + @Inject + private WxPayCommonService wxPayCommonService; + @Inject + private MemberPaymentService memberPaymentService; + + @At("") + @Ok("beetl:/platform/membercost/mine/index.html") + @RequiresPermissions("member.payment.mine") + public void index() { + } + + + @At + @ViReturn + @RequiresPermissions("member.payment.mine") + public Object pageData(PageForm pageForm, Integer year, Integer isPay) { + Sql sql = Sqls.create(""" + SELECT + mpp.`year`, + mpp.projectName, + mpp.startTime, + mpp.endTime, + mpi.id, + mpi.isPayment, + mpi.payPersonnel, + mpi.personType, + mpi.paymentMonth, + mpc.paymentStartMonth, + mpc.paymentEndMonth, + mpc.payStandards + FROM + `member_payment_info` mpi + LEFT JOIN member_payment_project mpp ON mpi.paymentProjectId = mpp.id + LEFT JOIN member_payment_config mpc ON mpc.id = mpp.configId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.andEX("YEAR(mpp.createTime)", "=", year); + cnd.andEX("mpi.userId", "=", ShiroUtil.getUserId()); + cnd.andEX("mpp.isDisabled", "=", 0); + if (ObjectUtil.isNotNull(isPay)) { + cnd.and("mpi.isPayment", "=", isPay); + } + sql.setCondition(cnd); + return memberPaymentService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + } + + + @At + @RepeatSubmit + @RequiresPermissions("member.payment.mine") + public Object pcOrderPay(Integer paymentMoney, String paymentInfoId) { + WxPayNeedParamVo paramVo = new WxPayNeedParamVo(); + paramVo.setDescription(ShiroUtil.getPlatformUsername() + "会费缴纳支付"); + paramVo.setAttach(""); + paramVo.setMemberPaymentInfoId(paymentInfoId); + paramVo.setAmount(new Amount().setTotal(paymentMoney)); + return wxPayCommonService.nativePay(paramVo); + } + + + @At + @RequiresPermissions("member.payment.mine") + public Object getIsPay(String paymentInfoId) { + int count = memberPaymentService.dao().count(MemberPaymentInfo.class, + Cnd.where("id", "=", paymentInfoId).and("isPayment", "=", 1)); + return Result.success().addData(count); + } +} diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentManageController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentProjectController.java similarity index 71% rename from src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentManageController.java rename to src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentProjectController.java index 4ad2167..4a42247 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentManageController.java +++ b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentProjectController.java @@ -1,4 +1,4 @@ -package io.v.nutz.zhgh.memberCost.controller.payment; +package io.v.nutz.zhgh.memberCost.controller; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateTime; @@ -23,9 +23,7 @@ import org.nutz.dao.sql.Sql; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.json.Json; import org.nutz.lang.Strings; -import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; @@ -37,28 +35,29 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @IocBean @Slf4j @Ok("json:full") -@At("/platform/member/payment/manage") -public class MemberPaymentManageController { +@At("/platform/member/payment/project") +public class MemberPaymentProjectController { @Inject private MemberPaymentService memberPaymentService; @At("") - @Ok("beetl:/platform/memberCost/payment/manage.html") - @RequiresPermissions("member.payment.manage") + @Ok("beetl:/platform/membercost/project.html") + @RequiresPermissions("member.payment.project") public void index() { } @At @ViReturn - @RequiresPermissions("member.payment.manage") + @RequiresPermissions("member.payment.project") public Object pageData(PageForm pageForm, @Param(value = "year", required = false) String year, @Param(value = "projectName", required = false) String projectName) { @@ -83,7 +82,7 @@ public class MemberPaymentManageController { */ @At @ViReturn - @RequiresPermissions("member.payment.manage") + @RequiresPermissions("member.payment.project") public Object openClosedProject(String id) { //位运算 取反 memberPaymentService.dao().update(MemberPaymentProject.class, @@ -101,7 +100,7 @@ public class MemberPaymentManageController { */ @At @ViReturn - @RequiresPermissions("member.payment.manage") + @RequiresPermissions("member.payment.project") public Object doSave(MemberPaymentProject paymentProject) { memberPaymentService.insertOrUpdate(paymentProject); return null; @@ -117,20 +116,22 @@ public class MemberPaymentManageController { */ @At @ViReturn - @RequiresPermissions("member.payment.manage") + @RequiresPermissions("member.payment.project") public Object doSetPayment(String id, Boolean isRenew) { //如果不是更新缴费人员,就删除该项目下的所有待缴费信息,再重新设置 if (!isRenew) { memberPaymentService.clear(Cnd.where("paymentProjectId", "=", id)); } + //获取缴费项目 + MemberPaymentProject project = memberPaymentService.dao().fetch(MemberPaymentProject.class, id); //获取缴费配置 - MemberPaymentConfig config = memberPaymentService.getConfig(); + MemberPaymentConfig config = memberPaymentService.getConfigById(project.getConfigId()); List payStandards = config.getPayStandards(); //缴费人员类型 List personTypeList = new ArrayList<>(); payStandards.forEach(v->{ - personTypeList.addAll(v.getPersonTypes()); + personTypeList.add(v.getPersonType()); }); //获取所有有入会时间的会员 @@ -140,23 +141,37 @@ public class MemberPaymentManageController { //过滤出需要新增到记录表中的人员 List needInsertUserIds = memberList.stream().filter(v -> !paymentUserIdList.contains(v.getId())).collect(Collectors.toList()); - //获取缴费项目 - MemberPaymentProject project = memberPaymentService.dao().fetch(MemberPaymentProject.class, id); - //该项目的缴费月份是几月到几月,转为数组,判断新入职教工应缴费几个月 - String[] strings = project.getPaymentMonth().substring(0, project.getPaymentMonth().length() - 1).split("-"); - DateTime beginDate = DateUtil.parse(project.getYear() + "-" + strings[0] + "-01"); + + //该缴费配置的缴费月份是几月到几月,转为数组 + String[] strings = {config.getPaymentStartMonth(),config.getPaymentEndMonth()}; + DateTime beginDate = DateUtil.parse(strings[0] + "-01"); //获取月份的最后一天 - DateTime endDate = DateUtil.endOfMonth(DateUtil.parse(project.getYear() + "-" + strings[1] + "-01")); + DateTime endDate = DateUtil.endOfMonth(DateUtil.parse(strings[1] + "-01")); List memberPaymentInfos = new ArrayList<>(); needInsertUserIds.forEach(v -> { MemberPaymentInfo paymentInfo = new MemberPaymentInfo(); + paymentInfo.setPersonType(v.getPersonType()); + long differMonth = 0; //如果是新入职教工,计算需要缴纳的月数 if (v.getMemberJoinTime().after(beginDate) && v.getMemberJoinTime().before(endDate)){ - long differMonth = DateUtil.betweenMonth(v.getMemberJoinTime(), endDate, false); + differMonth = DateUtil.betweenMonth(v.getMemberJoinTime(), endDate, false)+1; paymentInfo.setPaymentMonth(String.valueOf(differMonth)); - } else { - paymentInfo.setPaymentMonth(config.getPaidDuration()); + paymentInfo.setPayPersonnel("新进会员"); + //getPayment(v, payStandards, paymentInfo, differMonth,"新进会员"); + + }else if(v.getRetireDate()!=null&&v.getRetireDate().after(beginDate)&&v.getRetireDate().before(endDate)){ + //如果是退休会员 + differMonth = DateUtil.betweenMonth(beginDate, v.getRetireDate(), false)+1; + paymentInfo.setPaymentMonth(String.valueOf(differMonth)); + paymentInfo.setPayPersonnel("退休会员"); + //getPayment(v, payStandards, paymentInfo, differMonth,"退休会员"); + }else { + //如果是普通会员 + differMonth = DateUtil.betweenMonth(beginDate, endDate, false)+1; + paymentInfo.setPaymentMonth(String.valueOf(differMonth)); + paymentInfo.setPayPersonnel("普通会员"); + //getPayment(v, payStandards, paymentInfo, differMonth,"普通会员"); } paymentInfo.setUserId(v.getId()); paymentInfo.setLoginname(v.getLoginname()); @@ -197,6 +212,24 @@ public class MemberPaymentManageController { return null; } + private static void getPayment(Sys_user v, List payStandards, MemberPaymentInfo paymentInfo, long differMonth,String payPersonnel) { + List list = payStandards.stream().filter(v1 -> v1.getPersonType().equals(v.getPersonType()) && payPersonnel.equals(v1.getPayPersonnel())).collect(Collectors.toList()); + MemberPaymentConfig.PayStandards standards = null; + if (list!=null&&list.size()>0){ + standards = list.get(0); + } + if ("在编".equals(v.getPersonType())&&standards!=null){ + AtomicReference value= new AtomicReference<>(0); + standards.getPayModeValueList().forEach(v1->{ + value.set(value.get()+v1.getInt("value")); + }); + paymentInfo.setPaymentMoney(String.valueOf(value.get()*0.05* differMonth)); + } + if ("非编".equals(v.getPersonType())&&standards!=null){ + paymentInfo.setPaymentMoney(String.valueOf(Integer.valueOf(standards.getPayModeValue())* differMonth)); + } + } + /** * 删除 @@ -205,7 +238,7 @@ public class MemberPaymentManageController { * @return */ @At - @RequiresPermissions("member.payment.manage") + @RequiresPermissions("member.payment.project") @Aop(TransAop.READ_COMMITTED) public Object doRemove(String id) { int paymentCount = memberPaymentService.count(Cnd.where("paymentProjectId", "=", id).and("isPayment", "=", 1)); @@ -226,7 +259,7 @@ public class MemberPaymentManageController { */ @At @ViReturn - @RequiresPermissions("member.payment.manage") + @RequiresPermissions("member.payment.project") public Object getAllProject(String year) { CndPlus cnd = CndPlus.create(); cnd.and("isDisabled", "=", 0); @@ -246,16 +279,5 @@ public class MemberPaymentManageController { } - @At - @ViReturn - @RequiresPermissions("member.payment.manage") - public Object isMonthCreatedByYear(@Param("year") String year, @Param("monthSelect") String monthSelect, @Param("id") String projectId) { - List nutMapList = Json.fromJsonAsList(NutMap.class, monthSelect); - for (NutMap nutMap : nutMapList) { - int count = memberPaymentService.dao().count(MemberPaymentProject.class, Cnd.where("`year`", "=", year).and("paymentMonth", "=", nutMap.getString("label"))); - nutMap.setv("code", count > 0 ? 1 : 0); - } - return nutMapList; - } } diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentRefundController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentRefundController.java new file mode 100644 index 0000000..8560d0d --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentRefundController.java @@ -0,0 +1,114 @@ +package io.v.nutz.zhgh.memberCost.controller; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONObject; +import io.v.nutz.base.annontation.RepeatSubmit; +import io.v.nutz.base.annontation.Valid; +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.payment.enums.TradingStateEnum; +import io.v.nutz.base.payment.enums.WxPayResultCode; +import io.v.nutz.base.payment.model.PaymentTransaction; +import io.v.nutz.base.payment.wxpay.service.WxPayCommonService; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.base.result.Result; +import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Cnd; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +import java.io.Reader; + +/** + * @ClassName MemberPaymentRefundController + * @Description 退款 + * @Author zhf + * @Date 2025/3/11 上午8:51 + */ +@Slf4j +@IocBean +@Ok("json:full") +@At("/platform/member/payment/refund") +public class MemberPaymentRefundController { + + @Inject + private WxPayCommonService wxPayCommonService; + @Inject + private MemberPaymentService memberPaymentService; + + @At("") + @Ok("beetl:/platform/membercost/refund/index.html") + @RequiresPermissions("member.payment.refund") + public void index() { + } + + @At + @ViReturn + @RequiresPermissions("member.payment.refund") + public Object pageData(PageForm pageForm, String paymentProjectId, String transactionId, Integer isRefund) { + Sql sql = Sqls.create(""" + SELECT + mpp.`year`, + mpp.projectName, + mpp.startTime, + mpp.endTime, + mpi.isPayment, + mpi.payPersonnel, + mpi.personType, + mpi.paymentMonth, + mpi.paymentTime, + pt.id, + pt.outTradeNo, + pt.transactionId, + pt.userName, + pt.loginName, + pt.unitName, + pt.unionName, + pt.tradingAmount, + pt.isRefund, + prr.createdAt AS refundTime + FROM + payment_transaction pt + LEFT JOIN `member_payment_info` mpi ON pt.memberPaymentInfoId = mpi.id + LEFT JOIN `payment_refund_record` prr ON pt.memberPaymentInfoId = mpi.id + LEFT JOIN member_payment_project mpp ON mpi.paymentProjectId = mpp.id + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.andEX("mpi.paymentProjectId", "=", paymentProjectId); + cnd.andEX("pt.tradingState", "=", TradingStateEnum.PAY_SUCCESS); + cnd.and(Cnd.likeEX("pt.transactionId", transactionId)); + if (isRefund != 3) { + cnd.andEX("pt.isRefund", "=", isRefund); + } + if (ObjectUtil.isNotEmpty(pageForm.getSearchKeyword())) { + SqlExpressionGroup group = new SqlExpressionGroup(); + group.orLike("pt.userName", pageForm.getSearchKeyword()); + group.orLike("pt.loginName", pageForm.getSearchKeyword()); + cnd.and(group); + } + cnd.groupBy("pt.id"); + sql.setCondition(cnd); + return memberPaymentService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + } + + + @At + @RepeatSubmit + @RequiresPermissions("member.payment.refund") + public Object doRefund(@Valid String id) { + PaymentTransaction transaction = memberPaymentService.dao().fetch(PaymentTransaction.class, id); + Result refund = wxPayCommonService.refund(transaction.getTransactionId(), transaction.getOutTradeNo()); + return refund; + } + + +} diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentSummaryController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentSummaryController.java similarity index 92% rename from src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentSummaryController.java rename to src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentSummaryController.java index 0b60e6a..d7e61c9 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentSummaryController.java +++ b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentSummaryController.java @@ -1,4 +1,4 @@ -package io.v.nutz.zhgh.memberCost.controller.payment; +package io.v.nutz.zhgh.memberCost.controller; import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.ExcelImportUtil; @@ -66,7 +66,7 @@ public class MemberPaymentSummaryController { private MsgApi msgApi; @At("") - @Ok("beetl:/platform/memberCost/payment/summary.html") + @Ok("beetl:/platform/membercost/summary.html") @RequiresPermissions("member.payment.summary") public void index() { } @@ -75,7 +75,7 @@ public class MemberPaymentSummaryController { public Sql dataSql(String pageOrderName, String pageOrderBy, String searchName, String searchKeyword, String unionId, String unitId, String threeUnitId, String unionGroupId, String personType, String userState, String paymentProjectId, String year, - String isPay) { + String isPay,String schoolTime,String retireDate,String search) { Sql sql = Sqls.create(""" SELECT mpi.*, @@ -85,7 +85,6 @@ public class MemberPaymentSummaryController { u.sex, u.member, u.welfareMember, - u.personType, u.userState, u.unionname, u.unionid, @@ -95,7 +94,12 @@ public class MemberPaymentSummaryController { u.threeUnitCode, u.unionGroupName, u.mobile, - u.birthday + u.birthday, + u.schoolTime, + u.retireDate, + u.workDate, + startSalaryDate, + renewTime FROM `member_payment_info` mpi LEFT JOIN `user` u ON u.id = mpi.userId @@ -106,13 +110,16 @@ public class MemberPaymentSummaryController { if (StrUtil.isNotBlank(searchName) && StrUtil.isNotBlank(searchKeyword)) { cnd.and(Cnd.likeEX(searchName, searchKeyword)); } - if (!ShiroUtil.hasAnyRoles("sysadmin,A06,H03")) { + if (!ShiroUtil.hasAnyRoles("sysadmin,A06,H04")) { if (ShiroUtil.hasRole("H04")) { cnd.and("u.unionid", "=", Vi.getUnionId()); } else { cnd.and("u.unionGroupId", "in", io.v.nutz.web.commons.utils.ShiroUtil.getUnionGroupIds()); } } + if (Strings.isNotBlank(search)){ + cnd.orEX("u.username", "like","%" + search + "%"); + } cnd.andEX("mpp.`year`", "=", year); cnd.andEX("mpi.paymentProjectId", "=", paymentProjectId); if (Strings.isNotBlank(isPay) && !"null".equals(isPay)) { @@ -124,6 +131,8 @@ public class MemberPaymentSummaryController { cnd.andEX("u.unionGroupId", "=", unionGroupId); cnd.andEX("u.personType", "=", personType); cnd.andEX("u.userState", "=", userState); + cnd.andEX("u.schoolTime", "=", schoolTime); + cnd.andEX("u.retireDate", "=", retireDate); cnd.groupBy("u.id"); if (StrUtil.isNotBlank(pageOrderName) && StrUtil.isNotBlank(pageOrderBy)) { @@ -150,10 +159,13 @@ public class MemberPaymentSummaryController { @Param(value = "userState", required = false) String userState, @Param(value = "paymentProjectId", required = false) String paymentProjectId, @Param(value = "year", required = false) String year, - @Param(value = "isPay", required = false) String isPay) { + @Param(value = "isPay", required = false) String isPay, + @Param(value = "schoolTime", required = false) String schoolTime, + @Param(value = "retireDate", required = false) String retireDate, + @Param(value = "search", required = false) String search) { Sql sql = this.dataSql(pageForm.getPageOrderName(), pageForm.getPageOrderBy(), pageForm.getSearchName(), pageForm.getSearchKeyword(), unionId, unitId, threeUnitId, unionGroupId, - personType, userState, paymentProjectId, year, isPay); + personType, userState, paymentProjectId, year, isPay,schoolTime,retireDate,search); return memberPaymentService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); } @@ -164,7 +176,7 @@ public class MemberPaymentSummaryController { public Object modify(@Param("ids") String[] ids, String paymentMoney, boolean isPay, String remark) { List idList = Arrays.asList(ids); if (Lang.isNotEmpty(idList)) { - if (isPay) { + if (!isPay) { //未缴费设为已缴费 memberPaymentService.update(Chain.make("paymentMoney", paymentMoney) .add("remark", remark).add("isPayment", 1) @@ -285,9 +297,11 @@ public class MemberPaymentSummaryController { @Param(value = "paymentProjectId", required = false) String paymentProjectId, @Param(value = "year", required = false) String year, @Param(value = "isPay", required = false) String isPay, + @Param(value = "isPay", required = false) String schoolTime, + @Param(value = "isPay", required = false) String retireDate, boolean isZip, HttpServletResponse response) { Sql sql = this.dataSql(null, null, searchName, searchKeyword, unionId, unitId, threeUnitId, - unionGroupId, personType, userState, paymentProjectId, year, isPay); + unionGroupId, personType, userState, paymentProjectId, year, isPay,schoolTime,retireDate,""); List list = memberPaymentService.listMap(sql); NutMap nutMap = list.stream().findFirst().orElse(null); diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentTestController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentTestController.java new file mode 100644 index 0000000..0e5d030 --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentTestController.java @@ -0,0 +1,82 @@ +package io.v.nutz.zhgh.memberCost.controller; + +import io.v.nutz.base.payment.wxpay.service.WxPayCommonService; +import io.v.nutz.base.payment.wxpay.util.WeChatUtils; +import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Dao; +import org.nutz.integration.jedis.RedisService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +/** + * @version 1.0 + * @Author zzr + * @name:MemberPaymentTestController + * @Date 2025/3/10 14:55 + * @注释 + */ +@Slf4j +@IocBean +@Ok("json:full") +@At("/platform/member/payment/test") +public class MemberPaymentTestController { + + @Inject + private Dao dao; + @Inject + private WeChatUtils weChatUtils; + @Inject + private RedisService redisService; + @Inject + private WxPayCommonService wxPayCommonService; + @Inject + private MemberPaymentService memberPaymentService; + + @At("") + @Ok("beetl:/platform/membercost/test/index.html") + @RequiresPermissions("member.payment.test") + public void index() { + } + + + /** + * 微信jsapi支付需要网页授权认证 + * @param redirect + * @param userId + * @return + */ + @At + @Ok("re") + public String oauth(String redirect, String userId){ + String url = weChatUtils.getCodeUrl("https://schoolhd.top/platform/wxPay/getCode", userId); + redisService.setex("wx_pay_redirect_url", 60 * 60, "https://schoolhd.top/" + redirect); + return "redirect:" + url; + } + + /** + * 上面授权后会转到这个地址,然后去改数据库的openId, + * 后续对微信做一个免登录 + * @param code + * @param state + * @return + */ + @At + @Ok("re") + public String getCode(@Param("code") String code, @Param("state") String state){ +// String openId = wxPayCommonService.getOpenId(code); +// WxPayNeedParamVo paramVo = new WxPayNeedParamVo(); +// paramVo.setDescription("支付"); +// paramVo.setAttach(""); +// paramVo.setAmount(new Amount().setTotal(1)); +// paramVo.setPayer(new Payer().setOpenid(openId)); +// Result result = wxPayCommonService.jsApiPay(paramVo); + return null; +// return "redirect:" + redisService.get("wx_pay_redirect_url"); + } + +} diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentUserController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentUserController.java new file mode 100644 index 0000000..3eaa205 --- /dev/null +++ b/src/main/java/io/v/nutz/zhgh/memberCost/controller/MemberPaymentUserController.java @@ -0,0 +1,83 @@ +package io.v.nutz.zhgh.memberCost.controller; + + +import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.dao.CndPlus; +import io.v.nutz.base.query.PageForm; +import io.v.nutz.web.commons.utils.ShiroUtil; +import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo; +import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Cnd; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Strings; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +/** + * 个人缴费管理 + * @author hpf + */ +@IocBean +@Slf4j +@Ok("json:full") +@At("/platform/member/payment/user") +public class MemberPaymentUserController { + + @Inject + private MemberPaymentService memberPaymentService; + + @At("") + @Ok("beetl:/platform/membercost/user.html") + @RequiresPermissions("member.payment.user") + public void index() { + } + + @At + @ViReturn + @RequiresPermissions("member.payment.user") + public Object pageData(PageForm pageForm,@Param(value = "year", required = false) String year,@Param(value = "projectName", required = false) String projectName){ + Sql sql = Sqls.create(""" + SELECT + mpi.*, + mpp.`year`, + mpp.projectName, + mpp.startTime, + mpp.endTime, + mpp.configId, + mpc.payStandards, + mpc.paymentStartMonth, + mpc.paymentEndMonth + FROM + member_payment_info mpi + LEFT JOIN member_payment_project mpp ON mpi.paymentProjectId = mpp.id + LEFT JOIN member_payment_config mpc ON mpc.id = mpp.configId + $condition + """); + CndPlus cnd = CndPlus.create(); + + cnd.andEX("mpp.isDisabled", "=", false); + cnd.andEX("mpp.year", "=", year); + cnd.and("mpi.userId", "=", ShiroUtil.getUserId()); + if (Strings.isNotBlank(projectName)) { + cnd.and(Cnd.likeEX("mpp.projectName", projectName)); + } + cnd.desc("mpp.createTime"); + sql.setCondition(cnd); + return memberPaymentService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + } + + @At + @ViReturn + @RequiresPermissions("member.payment.user") + public Object doSave(MemberPaymentInfo memberPaymentInfo) { + memberPaymentService.insertOrUpdate(memberPaymentInfo); + return null; + } + +} diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentConfigController.java b/src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentConfigController.java deleted file mode 100644 index 7e4d016..0000000 --- a/src/main/java/io/v/nutz/zhgh/memberCost/controller/payment/MemberPaymentConfigController.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.v.nutz.zhgh.memberCost.controller.payment; - -import io.v.nutz.base.annontation.ViReturn; -import io.v.nutz.zhgh.memberCost.model.MemberPaymentConfig; -import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; -import lombok.extern.slf4j.Slf4j; -import org.apache.shiro.authz.annotation.RequiresPermissions; -import org.nutz.ioc.loader.annotation.Inject; -import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.mvc.annotation.At; -import org.nutz.mvc.annotation.Ok; - - -@IocBean -@Slf4j -@Ok("json:full") -@At("/platform/member/payment/config") -public class MemberPaymentConfigController { - - @Inject - private MemberPaymentService memberPaymentService; - - @At("") - @Ok("beetl:/platform/memberCost/payment/config.html") - @RequiresPermissions("member.payment.config") - public void index() {} - - - @At - @ViReturn - public Object getPaymentConfig(){ - return memberPaymentService.getConfig(); - } - - - @At - @ViReturn - @RequiresPermissions("member.payment.config") - public Object doAddOrModify(MemberPaymentConfig memberPaymentConfig){ - System.out.println(memberPaymentConfig); - memberPaymentService.dao().insertOrUpdate(memberPaymentConfig); - return null; - } -} diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/controller/mobile/MobileMemberCostListController.java b/src/main/java/io/v/nutz/zhgh/memberCost/h5controller/MobileMemberCostListController.java similarity index 67% rename from src/main/java/io/v/nutz/zhgh/memberCost/controller/mobile/MobileMemberCostListController.java rename to src/main/java/io/v/nutz/zhgh/memberCost/h5controller/MobileMemberCostListController.java index 8e3f55d..c2ca398 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/controller/mobile/MobileMemberCostListController.java +++ b/src/main/java/io/v/nutz/zhgh/memberCost/h5controller/MobileMemberCostListController.java @@ -1,6 +1,10 @@ -package io.v.nutz.zhgh.memberCost.controller.mobile; +package io.v.nutz.zhgh.memberCost.h5controller; +import com.ijpay.wxpay.model.v3.Amount; import io.v.nutz.base.annontation.ViReturn; +import io.v.nutz.base.payment.wxpay.service.WxPayCommonService; +import io.v.nutz.base.payment.wxpay.vo.WxPayNeedParamVo; +import io.v.nutz.base.result.Result; import io.v.nutz.web.commons.utils.ShiroUtil; import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; import lombok.extern.slf4j.Slf4j; @@ -25,6 +29,8 @@ public class MobileMemberCostListController { @Inject private MemberPaymentService memberPaymentService; + @Inject + private WxPayCommonService wxPayCommonService; @At("") @Ok("beetl:/mobile/memberCost/memberCostList.html") @@ -36,6 +42,10 @@ public class MobileMemberCostListController { public void paySuccess() { } + @At("/summary") + @Ok("beetl:/mobile/memberCost/summary.html") + public void summary() { + } @At @ViReturn @@ -62,5 +72,12 @@ public class MobileMemberCostListController { return memberPaymentService.fetch(id); } - + @At + public Object pullWeChatPay(Integer money, String busId) { + WxPayNeedParamVo wxPayNeedParamVo = new WxPayNeedParamVo(); + wxPayNeedParamVo.setDescription("会费缴纳"); + wxPayNeedParamVo.setMemberPaymentInfoId(busId); + wxPayNeedParamVo.setAmount(new Amount().setTotal(money)); + return wxPayCommonService.jsApiPay(wxPayNeedParamVo); + } } diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentConfig.java b/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentConfig.java index 6f7b9de..2bf4785 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentConfig.java +++ b/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentConfig.java @@ -1,5 +1,6 @@ package io.v.nutz.zhgh.memberCost.model; +import io.v.nutz.base.model.BaseModel; import lombok.Data; import org.nutz.dao.entity.annotation.*; import org.nutz.dao.interceptor.annotation.PrevInsert; @@ -10,7 +11,7 @@ import java.util.List; @Data @Table -public class MemberPaymentConfig { +public class MemberPaymentConfig extends BaseModel { @Column @Name @@ -19,6 +20,11 @@ public class MemberPaymentConfig { @PrevInsert(uu32 = true) private String id; + @Column + @Comment("缴费配置名称") + @ColDefine(type = ColType.VARCHAR,width = 50) + private String name; + @Column @Comment("缴费标准") @ColDefine(type = ColType.MYSQL_JSON) @@ -39,12 +45,26 @@ public class MemberPaymentConfig { @ColDefine(type = ColType.VARCHAR,width = 10) private String paidDuration; + @Column + @Comment("是否开启") + @ColDefine(type = ColType.BOOLEAN) + private boolean isDisabled; + + @Column + @Comment("开始缴费月份") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String paymentStartMonth; + + @Column + @Comment("结束缴费月份") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String paymentEndMonth; @Data public static class PayStandards implements Serializable { private String payPersonnel; private String payMode; - private List personTypes; + private String personType; private String payModeValue; private List payModeValueList; } diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentInfo.java b/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentInfo.java index a798ec0..b0b2c46 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentInfo.java +++ b/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentInfo.java @@ -87,4 +87,15 @@ public class MemberPaymentInfo { @Comment("缴费月份") @ColDefine(type = ColType.VARCHAR, width = 10) private String paymentMonth; + + @Column + @Comment("人员类型") + @ColDefine(type = ColType.VARCHAR, width = 10) + private String personType; + + + @Column + @Comment("会员类型") + @ColDefine(type = ColType.VARCHAR, width = 10) + private String payPersonnel; } diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentProject.java b/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentProject.java index a3ea16c..a8d890b 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentProject.java +++ b/src/main/java/io/v/nutz/zhgh/memberCost/model/MemberPaymentProject.java @@ -1,5 +1,6 @@ package io.v.nutz.zhgh.memberCost.model; +import io.v.nutz.base.model.BaseModel; import lombok.Data; import org.nutz.dao.entity.annotation.*; import org.nutz.dao.interceptor.annotation.PrevInsert; @@ -8,7 +9,7 @@ import java.util.Date; @Data @Table -public class MemberPaymentProject { +public class MemberPaymentProject extends BaseModel { @Column @Name @@ -39,7 +40,27 @@ public class MemberPaymentProject { @Column - @Comment("缴费月份") - @ColDefine(type = ColType.VARCHAR, width = 10) - private String paymentMonth; + @Comment("开始缴费月份") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String paymentStartMonth; + + @Column + @Comment("结束缴费月份") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String paymentEndMonth; + + @Column + @Comment("开始缴费时间") + @ColDefine(type = ColType.VARCHAR,width = 32) + private String startTime; + + @Column + @Comment("结束缴费时间") + @ColDefine(type = ColType.VARCHAR,width = 32) + private String endTime; + + @Column + @Comment("缴费配置id") + @ColDefine(type = ColType.VARCHAR,width = 32) + private String configId; } diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/service/MemberPaymentService.java b/src/main/java/io/v/nutz/zhgh/memberCost/service/MemberPaymentService.java index 7308adf..bd06971 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/service/MemberPaymentService.java +++ b/src/main/java/io/v/nutz/zhgh/memberCost/service/MemberPaymentService.java @@ -17,4 +17,8 @@ public interface MemberPaymentService extends ViService { Workbook exportOrZip(List list, OutputStream outputStream)throws Exception; MemberPaymentConfig getConfig(); + + List getPaymentConfigList(); + + MemberPaymentConfig getConfigById(String configId); } diff --git a/src/main/java/io/v/nutz/zhgh/memberCost/service/impl/MemberPaymentServiceImpl.java b/src/main/java/io/v/nutz/zhgh/memberCost/service/impl/MemberPaymentServiceImpl.java index 74683db..f87114a 100644 --- a/src/main/java/io/v/nutz/zhgh/memberCost/service/impl/MemberPaymentServiceImpl.java +++ b/src/main/java/io/v/nutz/zhgh/memberCost/service/impl/MemberPaymentServiceImpl.java @@ -11,6 +11,7 @@ import io.v.nutz.zhgh.memberCost.model.MemberPaymentProject; import io.v.nutz.zhgh.memberCost.service.MemberPaymentService; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Workbook; +import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.util.NutMap; @@ -61,4 +62,14 @@ public class MemberPaymentServiceImpl extends ViServiceImpl i public MemberPaymentConfig getConfig() { return dao().fetch(MemberPaymentConfig.class); } + + @Override + public List getPaymentConfigList() { + return dao().query(MemberPaymentConfig.class, Cnd.NEW().and("isDisabled","=","0")); + } + + @Override + public MemberPaymentConfig getConfigById(String configId) { + return dao().fetch(MemberPaymentConfig.class,configId); + } } diff --git a/src/main/java/io/v/nutz/zhgh/welfare/controller/WelfareProjectMangeController.java b/src/main/java/io/v/nutz/zhgh/welfare/controller/WelfareProjectMangeController.java index e16ec82..f28dd4e 100644 --- a/src/main/java/io/v/nutz/zhgh/welfare/controller/WelfareProjectMangeController.java +++ b/src/main/java/io/v/nutz/zhgh/welfare/controller/WelfareProjectMangeController.java @@ -1,5 +1,7 @@ package io.v.nutz.zhgh.welfare.controller; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import io.v.nutz.base.annontation.ViReturn; import io.v.nutz.base.dao.CndPlus; @@ -32,7 +34,6 @@ import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.Lang; import org.nutz.lang.Strings; import org.nutz.lang.random.R; -import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; @@ -94,7 +95,7 @@ public class WelfareProjectMangeController { Sql sql = Sqls.create("SELECT project.*,IF((SELECT count(1) FROM welfare_list WHERE projectId = project.id)>0,TRUE,FALSE) created FROM `welfare_project` project $condition"); CndPlus cnd = CndPlus.create(); - pageForm.defaultSortDesc("createdAt"); + pageForm.defaultSortDesc("opAt"); cnd.andEx("year", "=", year); if (Strings.isNotBlank(name)) { @@ -117,13 +118,14 @@ public class WelfareProjectMangeController { Trans.exec(() -> { + project.setYear(DateUtil.thisYear()); + project.setIsDisabled(false); if (project.getConditionStructure() != null) { project.setConditionStructureId(matchConditionStructureService.save(project.getConditionStructure()).getId()); } if (StrUtil.isBlank(project.getId())) { WelfareProject insertProject = dao.insertWith(project, "welfareProjectSubjects"); - if (Lang.isNotEmpty(insertProject.getWelfareProjectSubjects())) { insertProject.getWelfareProjectSubjects().forEach(v -> { dao.insertLinks(v, "options"); @@ -235,10 +237,8 @@ public class WelfareProjectMangeController { dao.update(project); } -// if (project.getIsPushHome() != 0) { -// mobileHomeService.handle(project, "welfare", "/mobile/welfare/list/receive?projectId=" + project.getId(), project.getIsPushHome()); -// } Sys_home_activity sysHomeActivity = project.covertToSysHomeActivity(); + sysHomeActivity.setEnable(true); dao.insertOrUpdate(sysHomeActivity); }); return null; @@ -256,7 +256,9 @@ public class WelfareProjectMangeController { projectService.dao().delete(MatchConditionStructure.class, project.getConditionStructureId()); } projectService.dao().clear(WelfareProjectSubject.class, Cnd.where("projectId", "=", id)); - projectService.dao().clear(WelfareProjectSubjectOption.class, Cnd.where("subjectId", "=", welfareProjectSubject.getId())); + if (ObjectUtil.isNotEmpty(welfareProjectSubject)) { + projectService.dao().clear(WelfareProjectSubjectOption.class, Cnd.where("subjectId", "=", welfareProjectSubject.getId())); + } projectService.dao().clear(WelfareUserSelection.class, Cnd.where("welfareId", "=", id)); projectService.delete(id); mobileHomeService.delete(id); @@ -292,7 +294,7 @@ public class WelfareProjectMangeController { public Object sendMsgToNotWelfareUsers(String projectId, String sendMsgValue) { Sql sql = Sqls.create(""" SELECT - u.loginname as userId + u.loginname FROM welfare_list wl left join sys_user u on u.id = wl.userId @@ -308,8 +310,13 @@ public class WelfareProjectMangeController { ) """); sql.setParam("projectId", projectId); - List userList = listService.listMap(sql); -// msgApi.sendMsg(sendMsgValue, "福利选择链接", "", "https://zhgh.jiangnan.edu.cn/mobile/welfare/list/receive?projectId=4fa233704ddb41bc8e34e966efeda0a4", "4", userList); +// List userList = listService.listMap(sql); +// Set loginNameList = userList.stream().map(user -> user.getString("loginname")).collect(Collectors.toSet()); +// String loginNames = loginNameList.stream().collect(Collectors.joining(",")); +// WelfareProject project = dao.fetch(WelfareProject.class, projectId); +// String link = Globals.AppDomain + "/mobile/welfare/list/receive?projectId=%s".formatted(projectId); +// String imageUrl = Globals.AppDomain + "/file_server/fileStreamPreview?id=" + project.getCover(); +// msgApi.sendMsg(List.of("DingTalk"), loginNames, 2, "职工福利", sendMsgValue, "", link); return null; } @@ -321,9 +328,11 @@ public class WelfareProjectMangeController { */ @At @ViReturn + @Aop(TransAop.READ_COMMITTED) @RequiresPermissions("welfare.list.mange") public Object projectStatusChange(String id, boolean val) { dao.update(WelfareProject.class, Chain.make("isDisabled", !val), Cnd.where("id", "=", id)); + dao.update(Sys_home_activity.class, Chain.make("enable", val), Cnd.where("id", "=", id)); return null; } diff --git a/src/main/java/io/v/nutz/zhgh/welfare/controller/WelfareUserChooseController.java b/src/main/java/io/v/nutz/zhgh/welfare/controller/WelfareUserChooseController.java index be6e065..eedf43e 100644 --- a/src/main/java/io/v/nutz/zhgh/welfare/controller/WelfareUserChooseController.java +++ b/src/main/java/io/v/nutz/zhgh/welfare/controller/WelfareUserChooseController.java @@ -55,27 +55,29 @@ public class WelfareUserChooseController { @ViReturn @RequiresPermissions("welfare.user.choose") public Object pageData(PageForm pageForm, Integer year) { + Sql sql = Sqls.create(""" SELECT - wp.*, - CASE - WHEN wus.selectUserId IS NOT NULL THEN 1 - ELSE 0 - END AS isChoose, - wus.selectTime, - GROUP_CONCAT(DISTINCT wuso.optionName ,'(',wus.selectNum,'份)') AS gist_list, - wus.receiveAddress + wp.*, + CASE + WHEN wus.selectUserId IS NOT NULL THEN 1 + ELSE 0 + END AS isChoose, + wus.selectTime, + GROUP_CONCAT(DISTINCT wuso.optionName ,'(',wus.selectNum,'份)') AS gist_list, + wus.receiveAddress FROM - welfare_project wp - LEFT JOIN welfare_project_user_selection wus ON wp.id = wus.welfareId AND wus.selectUserId = @selectUserId - INNER JOIN welfare_list wl ON wp.id = wl.projectId AND wl.userId = @selectUserId - LEFT JOIN welfare_project_subject_option wuso ON wus.selectOptionId = wuso.id + welfare_project wp + LEFT JOIN welfare_project_user_selection wus ON wp.id = wus.welfareId AND wus.selectUserId = @selectUserId + LEFT JOIN welfare_list wl ON wp.id = wl.projectId AND wl.userId = @selectUserId + LEFT JOIN welfare_project_subject_option wuso ON wus.selectOptionId = wuso.id $condition """); sql.setParam("selectUserId", ShiroUtil.getUserId()); Cnd cnd = Cnd.NEW(); cnd.and("wp.provideMode", "in", "2,3"); + cnd.and("wl.userId", "=", ShiroUtil.getUserId()); cnd.andEX("YEAR(wp.choiceTimeStart)", "in", year); cnd.and("wp.isDisabled", "=", 0); cnd.groupBy("wp.id"); diff --git a/src/main/java/io/v/nutz/zhgh/welfare/model/WelfareProject.java b/src/main/java/io/v/nutz/zhgh/welfare/model/WelfareProject.java index ab3e5c8..d012ecc 100644 --- a/src/main/java/io/v/nutz/zhgh/welfare/model/WelfareProject.java +++ b/src/main/java/io/v/nutz/zhgh/welfare/model/WelfareProject.java @@ -1,6 +1,6 @@ package io.v.nutz.zhgh.welfare.model; -import io.v.nutz.base.model.ViBaseModel; +import cn.wizzer.framework.base.model.BaseModel; import io.v.nutz.sys.models.Sys_home_activity; import io.v.nutz.sys.services.SysHomeConvert; import io.v.nutz.zhgh.data.model.MatchConditionStructure; @@ -29,7 +29,7 @@ import java.util.List; @Accessors(chain = true) @Table("welfare_project") @Comment("福利项目") -public class WelfareProject extends ViBaseModel implements Serializable, SysHomeConvert { +public class WelfareProject extends BaseModel implements Serializable, SysHomeConvert { private static final long serialVersionUID = 1L; @@ -43,9 +43,6 @@ public class WelfareProject extends ViBaseModel implements Serializable, SysHome @Column @Comment("年度") @ColDefine(type = ColType.INT, width = 4) - @PrevInsert( - els = {@EL("$me.nowYear()")} - ) private Integer year; @Column diff --git a/src/main/java/io/v/nutz/zhgh/welfare/service/impl/WelfareSingleServiceImpl.java b/src/main/java/io/v/nutz/zhgh/welfare/service/impl/WelfareSingleServiceImpl.java index d6e3490..c1a8fa7 100644 --- a/src/main/java/io/v/nutz/zhgh/welfare/service/impl/WelfareSingleServiceImpl.java +++ b/src/main/java/io/v/nutz/zhgh/welfare/service/impl/WelfareSingleServiceImpl.java @@ -1,10 +1,10 @@ package io.v.nutz.zhgh.welfare.service.impl; -import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.annotation.Excel; import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.excel.export.ExcelExportService; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; @@ -21,6 +21,7 @@ import io.v.nutz.zhgh.welfare.model.WelfareProject; import io.v.nutz.zhgh.welfare.model.template.WelfareExportEntityTc; import io.v.nutz.zhgh.welfare.service.WelfareSingleService; import lombok.Data; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; @@ -46,9 +47,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** @@ -671,23 +670,25 @@ public class WelfareSingleServiceImpl extends ViServiceImpl impl SELECT u.loginname, u.username, - vwl.welfareUnitId as unionid, - gh.unionname as unionname, + IF(staffUnion.id IS NOT NULL, staffUnion.id, vwl.welfareUnitId) AS unionid, + IF(staffUnion.id IS NOT NULL, staffUnion.unionname, gh.unionname) AS unionname, vwl.welfareSecondLevelUnitId as unitid, dw.`name` as unitname, wpus.userSign, wpus.selectOptionId, - wpus.selectNum\s + wpus.selectNum FROM welfare_list vwl - LEFT join sys_user u on u.id = vwl.userId - left join sys_union gh on gh.id = vwl.welfareUnitId - left join sys_unit dw on dw.id = vwl.welfareSecondLevelUnitId - LEFT JOIN welfare_project_user_selection wpus ON wpus.selectUserId = vwl.userId\s - AND wpus.welfareId = @projectId\s + LEFT JOIN special_staff staff ON staff.userId = vwl.userId + LEFT JOIN sys_union staffUnion ON staff.unionRelationUnionId = staffUnion.id + LEFT JOIN sys_user u ON u.id = vwl.userId + LEFT JOIN sys_union gh ON gh.id = vwl.welfareUnitId + LEFT JOIN sys_unit dw ON dw.id = vwl.welfareSecondLevelUnitId + LEFT JOIN welfare_project_user_selection wpus ON wpus.selectUserId = vwl.userId + AND wpus.welfareId = @projectId WHERE projectId = @projectId - AND vwl.welfareUnitId = @unionId + AND (vwl.welfareUnitId = @unionId OR staffUnion.id = @unionId) order by vwl.welfareUnitId,vwl.welfareSecondLevelUnitId """); @@ -712,18 +713,21 @@ public class WelfareSingleServiceImpl extends ViServiceImpl impl }); List exportEntities = new ArrayList<>(); - exportEntities.add(new ExcelExportEntity("序号", "no", 20)); - exportEntities.add(new ExcelExportEntity("姓名", "username", 20)); - exportEntities.add(new ExcelExportEntity("工号", "loginname", 20)); - exportEntities.add(new ExcelExportEntity("所在基层工会", "unionname", 20)); - exportEntities.add(new ExcelExportEntity("所在单位", "unitname", 20)); + exportEntities.add(new ExcelExportEntity("序号", "no", 10)); + exportEntities.add(new ExcelExportEntity("姓名", "username", 10)); + exportEntities.add(new ExcelExportEntity("工号", "loginname", 15)); +// exportEntities.add(new ExcelExportEntity("所在分工会", "unionname", 20)); + ExcelExportEntity excelExportEntity1 = new ExcelExportEntity("所在单位", "unitname", 20); + excelExportEntity1.setWrap(true); + exportEntities.add(excelExportEntity1); for (NutMap option : options) { - exportEntities.add(new ExcelExportEntity(option.getString("optionName"), option.getString("id"), 20)); + exportEntities.add(new ExcelExportEntity(option.getString("optionName"), option.getString("id"), 10)); } ExcelExportEntity excelExportEntity = new ExcelExportEntity(); excelExportEntity.setName("签字"); excelExportEntity.setKey("qzBytes"); + excelExportEntity.setHeight(12); excelExportEntity.setWidth(20); excelExportEntity.setType(2); excelExportEntity.setExportImageType(2); @@ -764,9 +768,16 @@ public class WelfareSingleServiceImpl extends ViServiceImpl impl totalMap.put(option.getString("id"), selectNum); } dataList.add(totalMap); - ExportParams exportParams = new ExportParams(); + /*ExportParams exportParams = new ExportParams(); exportParams.setType(ExcelType.XSSF); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, dataList); + workbook.write(outputStream);*/ + + Workbook workbook = new HSSFWorkbook(); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.HSSF); + ExcelExportService service = new ExcelExportService(); + service.createSheetForMap(workbook, exportParams, exportEntities, dataList); workbook.write(outputStream); // List result = new ArrayList<>(); @@ -1023,7 +1034,6 @@ public class WelfareSingleServiceImpl extends ViServiceImpl impl sheet.setColumnWidth(0, 1000 * 2); sheet.setColumnWidth(1, 1500 * 2); sheet.setColumnWidth(2, 3000 * 2); - sheet.setColumnWidth(2, 3000 * 2); sheet.setColumnWidth(3, 2000 * 2); sheet.setColumnWidth(4, 2000 * 2); sheet.setColumnWidth(5, 1500 * 2); @@ -1180,18 +1190,43 @@ public class WelfareSingleServiceImpl extends ViServiceImpl impl } else { v.setQzBytes(null); } - } + + Map addressMap = new HashMap<>(); + String receiveAddress = v.getReceiveAddress(); + if (StrUtil.isNotBlank(receiveAddress)) { + List addressList = Arrays.stream(receiveAddress.split("[,,]")).collect(Collectors.toList()); + for (String pair : addressList) { + // 2. 根据冒号分割键和值 + String[] keyValue = pair.split("[::]", 2); + if (keyValue.length == 2) { + String key = keyValue[0].trim(); + String value = keyValue[1].trim(); + addressMap.put(key, value); + } + } +// v.setRecipient(addressMap.get("收件人")); +// v.setPhone(addressMap.get("联系方式")); +// v.setAddress(addressMap.get("收货地址")); + } + }); ExportParams exportParams = new ExportParams(); - exportParams.setType(ExcelType.XSSF); + exportParams.setType(ExcelType.HSSF); exportParams.setSheetName(project.getName()); try { - Workbook workbook = ExcelExportUtil.exportExcel(exportParams, WelfareExportEntityTc.class, welfareExportEntityTcList); - ViTool.excelResponse(response, (project.getName() + (flag ? "已选名单" : "未选名单")) + ".xlsx"); + ViTool.excelResponse(response, (project.getName() + (flag ? "已选名单" : "未选名单")) + ".xls"); + Workbook workbook = new HSSFWorkbook(); + ExcelExportService service = new ExcelExportService(); + service.createSheet(workbook, exportParams, WelfareExportEntityTc.class, welfareExportEntityTcList); ServletOutputStream outputStream = response.getOutputStream(); workbook.write(outputStream); - outputStream.close(); + + +// Workbook workbook = ExcelExportUtil.exportExcel(exportParams, WelfareExportEntityTc.class, welfareExportEntityTcList); +// ServletOutputStream outputStream = response.getOutputStream(); +// workbook.write(outputStream); +// outputStream.close(); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c530690..551a601 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -47,7 +47,7 @@ jdbc.defaultAutoCommit=true xsssql.ignore.urls=/platform/cms/content/article/addDo,/platform/sys/app/conf/addDo,/platform/sys/app/conf/editDo,\ /platform/member/change/mange/userPageData,/platform/welfare/member/mange/userPageData,/platform/sys/task/cronExp,\ /platform/sys/task/editDo,/platform/sys/task/addDo,/platform/fellowship/manage/activity/doHandle,/platform/sys/menu/editMenuDo,\ - /platform/trainSingUp/manage/activity/doHandle + /platform/trainSingUp/manage/activity/doHandle,/platform/activity/school/info/mange/doAdd,/platform/activity/school/info/mange/doEdit #环境 dev/prod v.environment=dev v.loginUrl=/sso/login @@ -78,7 +78,7 @@ shiro.url.login=/platform shiro.url.logout_redirect=/platform shiro.url.unauth=/platform #开发时设置本地路径,便于调试 -beetl.RESOURCE.rootLocal=G:/zhgh/zhgh_hrbnu/src/main/resources/views/ +beetl.RESOURCE.rootLocal=G:/zhgh/ /src/main/resources/views/ beetl.RESOURCE.root=views/ beetl.DELIMITER_STATEMENT_START= @@ -143,5 +143,15 @@ logback.exts.loglevel.enabled=true #心跳间隔(单位:秒) logback.exts.loglevel.heartbeat=15 +wxpay.appId=\u5E94\u7528\u7F16\u53F7 +wxpay.keyPath=key.pem +wxpay.certPath=cert.pem +wxpay.certP12Path=cert.p12 +wxpay.platformCertPath=wx_cert.pem +wxpay.mchId=\u5FAE\u4FE1\u5546\u6237\u53F7 +wxpay.apiKey3=Api-v3 \u5BC6\u94A5 +wxpay.apiKey=Api \u5BC6\u94A5 +wxpay.domain=https://ijpay.javen.com + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 8128e03..86e2312 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -32,9 +32,9 @@ - + - \ No newline at end of file + diff --git a/src/main/resources/static/assets/mobile/svg/memberPay/icon_xsgj.svg b/src/main/resources/static/assets/mobile/svg/memberPay/icon_xsgj.svg new file mode 100644 index 0000000..967d95d --- /dev/null +++ b/src/main/resources/static/assets/mobile/svg/memberPay/icon_xsgj.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/static/assets/mobile/svg/memberPay/pcPaySuccess.svg b/src/main/resources/static/assets/mobile/svg/memberPay/pcPaySuccess.svg new file mode 100644 index 0000000..57ee4a3 --- /dev/null +++ b/src/main/resources/static/assets/mobile/svg/memberPay/pcPaySuccess.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/static/assets/mobile/svg/memberPay/wx_img.png b/src/main/resources/static/assets/mobile/svg/memberPay/wx_img.png new file mode 100644 index 0000000..dd3d9e2 Binary files /dev/null and b/src/main/resources/static/assets/mobile/svg/memberPay/wx_img.png differ diff --git a/src/main/resources/static/components/sign/sign.vue b/src/main/resources/static/components/sign/sign.vue index 0e15416..25aa749 100644 --- a/src/main/resources/static/components/sign/sign.vue +++ b/src/main/resources/static/components/sign/sign.vue @@ -84,8 +84,17 @@ module.exports = { clearInterval(signatureInterval) const ts = new Date().getTime() + (Math.floor(Math.random() * (1000 - 1 + 1)) + 1).toString() console.log(ts) - // this.postAddress = location.protocol + '//' + location.host + '/signature?prefix=' + this.prefix + '&id=' + ts + "&is_value_base64=" + this.is_value_base64 - this.postAddress = "https://zhgh.hrbnu.edu.cn/signature?prefix=" + this.prefix + "&id=" + ts + "&is_value_base64=" + this.is_value_base64 + this.postAddress = + location.protocol + + "//" + + location.host + + "/signature?prefix=" + + this.prefix + + "&id=" + + ts + + "&is_value_base64=" + + this.is_value_base64 + // this.postAddress = "https://zhgh.hrbnu.edu.cn/signature?prefix=" + this.prefix + "&id=" + ts + "&is_value_base64=" + this.is_value_base64 this.showQrCode = true diff --git a/src/main/resources/views/layouts/platform.html b/src/main/resources/views/layouts/platform.html index 03123ce..95ec4c3 100644 --- a/src/main/resources/views/layouts/platform.html +++ b/src/main/resources/views/layouts/platform.html @@ -101,7 +101,6 @@ - @@ -109,6 +108,7 @@ + diff --git a/src/main/resources/views/mobile/memberCost/memberCostList.html b/src/main/resources/views/mobile/memberCost/memberCostList.html index c5823d0..6e631d4 100644 --- a/src/main/resources/views/mobile/memberCost/memberCostList.html +++ b/src/main/resources/views/mobile/memberCost/memberCostList.html @@ -3,45 +3,70 @@ layout("/mobile/platform.html"){ #-->
@@ -49,255 +74,311 @@ layout("/mobile/platform.html"){ -
- - - - + + + + - - - - - -
-
- - - - - - +
+
+
+
+
-
- - - - - - - - - - - - +
+
+ 项目名称: + {{item.projectName}} +
+
+ 所属时间: + {{item.paymentStartMonth}} 至 {{item.paymentEndMonth}} +
+
+ 缴费时间: + {{moment(item.startTime).format('MM-DD') + ' 至 ' + moment(item.endTime).format('MM-DD')}} +
- - - - - - - + +
+ + +
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ diff --git a/src/main/resources/views/mobile/memberCost/summary.html b/src/main/resources/views/mobile/memberCost/summary.html new file mode 100644 index 0000000..762e9c0 --- /dev/null +++ b/src/main/resources/views/mobile/memberCost/summary.html @@ -0,0 +1,198 @@ + +
+ + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ 提交 +
+
+
+
+
+
+ + + diff --git a/src/main/resources/views/mobile/welfare/list/receive.html b/src/main/resources/views/mobile/welfare/list/receive.html index 63d0f47..f871108 100644 --- a/src/main/resources/views/mobile/welfare/list/receive.html +++ b/src/main/resources/views/mobile/welfare/list/receive.html @@ -16,11 +16,13 @@ layout("/mobile/platform.html"){ border-bottom: 1px dashed lightgrey; } - .selection .van-checkbox-group .van-cell, .selection .van-radio-group .van-cell { + .selection .van-checkbox-group .van-cell, + .selection .van-radio-group .van-cell { flex-direction: row-reverse; } - .selection .van-checkbox-group .van-cell .van-cell__title, .selection .van-radio-group .van-cell .van-cell__title { + .selection .van-checkbox-group .van-cell .van-cell__title, + .selection .van-radio-group .van-cell .van-cell__title { padding-left: 20px; } @@ -41,7 +43,6 @@ layout("/mobile/platform.html"){ } .van-collapse-item .van-collapse-item__title { - } .selection .van-radio { @@ -59,7 +60,9 @@ layout("/mobile/platform.html"){ height: 80px; } - .welfare_img .van-image__error, .welfare_img .van-image__img, .welfare_img .van-image__loading { + .welfare_img .van-image__error, + .welfare_img .van-image__img, + .welfare_img .van-image__loading { border-radius: 10px; margin-left: -35px; } @@ -115,7 +118,7 @@ layout("/mobile/platform.html"){ padding-left: 20px; padding-right: 10px; position: relative; - height: 100px + height: 100px; } .optionDesc { @@ -132,142 +135,143 @@ layout("/mobile/platform.html"){ border: 1pt solid #000000 !important; } + .van-submit-bar__button { + width: 150px; + } + + .custom-cell { + font-size: 18px; + font-weight: bold; + }
- + - +
-
- 配送至 -
+
配送至
- - - + + - + + :options="dzOptions" + :props="{ expandTrigger: 'hover' }" + @change="handleChange" + clearable + placeholder="请选择地址信息" + style="width: 100%" + v-model="formData.addressList" + > - - + + - - + + - - + + + + + + + + - 取 消 - 保存 - + 取 消 + 保存 + -
- - diff --git a/src/main/resources/views/platform/welfare/mine.html b/src/main/resources/views/platform/welfare/mine.html index 3b1dc3f..a58078a 100644 --- a/src/main/resources/views/platform/welfare/mine.html +++ b/src/main/resources/views/platform/welfare/mine.html @@ -25,26 +25,24 @@ layout("/layouts/platform.html"){ font-size: 13px; color: #868282; } - -
- - + - + - - + + {{item.optionName}} - 未评价 + 未评价 - -
- +
+
- - + 关 闭 - 提 交 - + 提 交 + - +
- + + :color="item.color" + :key="index" + :timestamp="moment().format('YYYY-MM-DD HH:mm:ss')" + v-for="(item, index) in logisticsTrace.logisticsTraceDetailList" + > {{item.desc}} - - @@ -353,7 +296,7 @@ layout("/layouts/platform.html"){
diff --git a/src/main/resources/views/platform/welfare/statistics/single.html b/src/main/resources/views/platform/welfare/statistics/single.html index 390ae0c..5ad2182 100644 --- a/src/main/resources/views/platform/welfare/statistics/single.html +++ b/src/main/resources/views/platform/welfare/statistics/single.html @@ -65,6 +65,20 @@ layout("/layouts/platform.html"){
+
+
人员类型:
+
+ +
+
+
搜索
@@ -86,7 +100,7 @@ layout("/layouts/platform.html"){ type="primary" v-if="judgmentAuthority" > - 导出各基层工会福利签领表 + 导出各分工会福利签领表 按品牌导出全部(蛋糕券) --> -