From ffc9fcb95c183f38d17d9372f259b0dea2652894 Mon Sep 17 00:00:00 2001 From: songmingsong <2929511417@qq.com> Date: Thu, 5 Dec 2024 17:33:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98=E3=80=81?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E3=80=81=E8=AE=A2=E5=8D=95=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=EF=BC=9B=20=E5=BE=AE=E4=BF=A1=E7=94=A8=E6=88=B7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E3=80=81=E7=94=A8=E6=88=B7=E4=BF=A1=E6=81=AF=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E3=80=81=E4=BF=AE=E6=94=B9=E7=94=A8=E6=88=B7=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E3=80=81=E5=90=8C=E6=84=8F=E7=94=A8=E6=88=B7=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=EF=BC=9B=20=E6=96=87=E4=BB=B6OSS=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E3=80=81=E5=88=A0=E9=99=A4=E6=8E=A5=E5=8F=A3=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 +- .../java/com/ycwl/basic/config/OssConfig.java | 29 ++ .../com/ycwl/basic/config/WechatConfig.java | 74 +++ .../ycwl/basic/constant/NumberConstant.java | 62 +++ .../ycwl/basic/constant/WeiXinConstant.java | 82 ++++ .../ycwl/basic/controller/FileController.java | 50 ++ .../controller/mobile/AppFaceController.java | 3 +- .../mobile/AppMemberController.java | 75 ++- .../controller/mobile/AppWxPayController.java | 48 ++ .../com/ycwl/basic/enums/AgreementEnum.java | 20 + .../ycwl/basic/enums/AppStatesCodeEnum.java | 36 -- .../com/ycwl/basic/enums/BizCodeEnum.java | 436 +++++++++++++++++- .../com/ycwl/basic/enums/OrderStateEnum.java | 25 + .../ycwl/basic/enums/WechatErrorCodeEnum.java | 47 ++ .../ycwl/basic/exception/AppException.java | 9 +- .../exception/CustomExceptionHandle.java | 2 +- .../com/ycwl/basic/model/jwt/JwtInfo.java | 1 + .../model/mobile/DTO/WeChatUserInfoDTO.java | 42 ++ .../mobile/DTO/WeChatUserInfoUpdateDTO.java | 26 ++ .../model/mobile/WeChatUserInfoModel.java | 34 ++ .../model/pc/member/entity/MemberEntity.java | 4 + .../basic/model/wxPay/WXPayOrderReqVO.java | 29 ++ .../ycwl/basic/model/wxPay/WxPayRespVO.java | 41 ++ .../wxPay/WxchatCallbackSuccessData.java | 69 +++ .../com/ycwl/basic/service/FileService.java | 29 ++ .../com/ycwl/basic/service/HttpService.java | 121 +++++ .../impl/mobile/AppMemberServiceImpl.java | 141 ++++++ .../service/impl/mobile/WxPayServiceImpl.java | 201 ++++++++ .../service/impl/pc/OrderServiceImpl.java | 27 ++ .../service/mobile/AppMemberService.java | 52 +++ .../basic/service/mobile/WxPayService.java | 25 + .../ycwl/basic/service/pc/OrderService.java | 5 +- .../com/ycwl/basic/utils/ApiResponse.java | 4 +- .../com/ycwl/basic/utils/HttpServiceUtil.java | 31 ++ .../com/ycwl/basic/utils/JwtTokenUtil.java | 51 -- .../java/com/ycwl/basic/utils/OssUtil.java | 85 ++++ .../java/com/ycwl/basic/utils/SslUtil.java | 98 ++++ .../java/com/ycwl/basic/utils/WXPayUtil.java | 49 ++ src/main/resources/application.yml | 37 +- 39 files changed, 2074 insertions(+), 133 deletions(-) create mode 100644 src/main/java/com/ycwl/basic/config/OssConfig.java create mode 100644 src/main/java/com/ycwl/basic/config/WechatConfig.java create mode 100644 src/main/java/com/ycwl/basic/constant/NumberConstant.java create mode 100644 src/main/java/com/ycwl/basic/constant/WeiXinConstant.java create mode 100644 src/main/java/com/ycwl/basic/controller/FileController.java create mode 100644 src/main/java/com/ycwl/basic/controller/mobile/AppWxPayController.java create mode 100644 src/main/java/com/ycwl/basic/enums/AgreementEnum.java delete mode 100644 src/main/java/com/ycwl/basic/enums/AppStatesCodeEnum.java create mode 100644 src/main/java/com/ycwl/basic/enums/OrderStateEnum.java create mode 100644 src/main/java/com/ycwl/basic/enums/WechatErrorCodeEnum.java create mode 100644 src/main/java/com/ycwl/basic/model/mobile/DTO/WeChatUserInfoDTO.java create mode 100644 src/main/java/com/ycwl/basic/model/mobile/DTO/WeChatUserInfoUpdateDTO.java create mode 100644 src/main/java/com/ycwl/basic/model/mobile/WeChatUserInfoModel.java create mode 100644 src/main/java/com/ycwl/basic/model/wxPay/WXPayOrderReqVO.java create mode 100644 src/main/java/com/ycwl/basic/model/wxPay/WxPayRespVO.java create mode 100644 src/main/java/com/ycwl/basic/model/wxPay/WxchatCallbackSuccessData.java create mode 100644 src/main/java/com/ycwl/basic/service/FileService.java create mode 100644 src/main/java/com/ycwl/basic/service/HttpService.java create mode 100644 src/main/java/com/ycwl/basic/service/impl/mobile/AppMemberServiceImpl.java create mode 100644 src/main/java/com/ycwl/basic/service/impl/mobile/WxPayServiceImpl.java create mode 100644 src/main/java/com/ycwl/basic/service/mobile/AppMemberService.java create mode 100644 src/main/java/com/ycwl/basic/service/mobile/WxPayService.java create mode 100644 src/main/java/com/ycwl/basic/utils/HttpServiceUtil.java create mode 100644 src/main/java/com/ycwl/basic/utils/OssUtil.java create mode 100644 src/main/java/com/ycwl/basic/utils/SslUtil.java create mode 100644 src/main/java/com/ycwl/basic/utils/WXPayUtil.java diff --git a/pom.xml b/pom.xml index ecabfb7..1fbb567 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,12 @@ - + + + com.github.wechatpay-apiv3 + wechatpay-java + 0.2.12 + org.springframework.boot diff --git a/src/main/java/com/ycwl/basic/config/OssConfig.java b/src/main/java/com/ycwl/basic/config/OssConfig.java new file mode 100644 index 0000000..72d8b1a --- /dev/null +++ b/src/main/java/com/ycwl/basic/config/OssConfig.java @@ -0,0 +1,29 @@ +package com.ycwl.basic.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 阿里云OSS配置 + * + * @author songmingsong + **/ +@Data +@Component +public class OssConfig { + @Value("${aliYunOss.endpoint}") + private String endPoint; + @Value("${aliYunOss.accessKeyId}") + private String accessKeyId; + @Value("${aliYunOss.accessKeySecret}") + private String accessKeySecret; + @Value("${aliYunOss.bucketName}") + private String bucketName; + @Value("${aliYunOss.objectName}") + private String objectName; + @Value("${aliYunOss.url}") + private String url; + @Value("${aliYunOss.region}") + private String region; +} diff --git a/src/main/java/com/ycwl/basic/config/WechatConfig.java b/src/main/java/com/ycwl/basic/config/WechatConfig.java new file mode 100644 index 0000000..1adee77 --- /dev/null +++ b/src/main/java/com/ycwl/basic/config/WechatConfig.java @@ -0,0 +1,74 @@ +package com.ycwl.basic.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 微信小程序配置 + * + * @author songmingsong + **/ +@Data +@Component +@ConfigurationProperties(prefix = "wx") +public class WechatConfig { + + /** + * 公众号的appId + */ + private String appId; + + /** + * 公众号的密钥 + */ + private String appSecret; + + /** + * 公众号推送模板 + */ + @Value("${wx.push.templateId}") + private String templateId; + + /** + * 小程序的AppId + */ + private String miniProgramAppId; + + /** + * 小程序的secret + */ + private String miniProgramSecret; + + /** + * 申请openid授权 + */ + private String grandType; + + /** + * 商户号 + */ + private String mchId; + + /** + * 商户证书序列号 + */ + private String mchSerialNo; + + /** + * 回调接口地址 + */ + private String notifyUrl; + + /** + * 商户API私钥路径 + */ + private String keyPath; + + /** + * 商户APIV3密钥 + */ + private String apiV3; + +} diff --git a/src/main/java/com/ycwl/basic/constant/NumberConstant.java b/src/main/java/com/ycwl/basic/constant/NumberConstant.java new file mode 100644 index 0000000..a4825d3 --- /dev/null +++ b/src/main/java/com/ycwl/basic/constant/NumberConstant.java @@ -0,0 +1,62 @@ +package com.ycwl.basic.constant; + +/** + *

数字常量类

+ * + * @author songmingsong + * @Description 数字常量类 + */ +public interface NumberConstant { + + int NEGATIVE_ONE = -1; + int NEGATIVE_TWO = -2; + + int ZERO = 0; + + int ONE = 1; + + double ONE_DOUBLE = 1.0; + + int TWO = 2; + + int THREE = 3; + + int FOUR = 4; + + int FIVE = 5; + + int SIX = 6; + + int SEVEN = 7; + + int TEN = 10; + + int ELEVEN = 11; + + int SIXTEEN = 16; + + int SEVENTEEN = 17; + + int EIGHTEEN = 18; + + int NINETEEN = 19; + + int TWENTY = 20; + + int TWENTY_FOUR = 24; + + int TWENTY_FIVE = 25; + + int THIRTY = 30; + + int FORTY = 40; + + int SIXTY = 60; + + int HUNDRED = 100; + + int THOUSAND = 1000; + + int MILLION = 1000000; + +} diff --git a/src/main/java/com/ycwl/basic/constant/WeiXinConstant.java b/src/main/java/com/ycwl/basic/constant/WeiXinConstant.java new file mode 100644 index 0000000..49a92d4 --- /dev/null +++ b/src/main/java/com/ycwl/basic/constant/WeiXinConstant.java @@ -0,0 +1,82 @@ +package com.ycwl.basic.constant; + +import com.wechat.pay.java.service.payments.model.Transaction; + +import java.util.HashMap; +import java.util.Map; + +/** + *

@description: 微信常量

+ *

@author: songmingsong

+ **/ +public class WeiXinConstant { + + private static final Map STATE_DESCRIPTION_MAP = new HashMap<>(); + + static { + STATE_DESCRIPTION_MAP.put(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.SUCCESS, "SUCCESS"); + STATE_DESCRIPTION_MAP.put(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.REFUND, "REFUND"); + STATE_DESCRIPTION_MAP.put(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.NOTPAY, "NOTPAY"); + STATE_DESCRIPTION_MAP.put(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.CLOSED, "CLOSED"); + STATE_DESCRIPTION_MAP.put(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.REVOKED, "REVOKED"); + STATE_DESCRIPTION_MAP.put(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.USERPAYING, "USERPAYING"); + STATE_DESCRIPTION_MAP.put(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.PAYERROR, "PAYERROR"); + STATE_DESCRIPTION_MAP.put(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.ACCEPT, "ACCEPT"); + } + + public static String getDescriptionState(com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum state) { + return STATE_DESCRIPTION_MAP.getOrDefault(state, "未知状态"); + } + + private static final Map STATE_DESCRIPTION_MAP_TYPE = new HashMap<>(); + + static { + STATE_DESCRIPTION_MAP_TYPE.put(Transaction.TradeTypeEnum.JSAPI, "JSAPI"); + STATE_DESCRIPTION_MAP_TYPE.put(Transaction.TradeTypeEnum.NATIVE, "NATIVE"); + STATE_DESCRIPTION_MAP_TYPE.put(Transaction.TradeTypeEnum.APP, "APP"); + STATE_DESCRIPTION_MAP_TYPE.put(Transaction.TradeTypeEnum.MICROPAY, "MICROPAY"); + STATE_DESCRIPTION_MAP_TYPE.put(Transaction.TradeTypeEnum.MWEB, "MWEB"); + STATE_DESCRIPTION_MAP_TYPE.put(Transaction.TradeTypeEnum.FACEPAY, "FACEPAY"); + } + + public static String getDescriptionType(Transaction.TradeTypeEnum type) { + return STATE_DESCRIPTION_MAP_TYPE.getOrDefault(type, "未知类型"); + } + + + /** + * 公众号模板地址 + */ + public static final String PUBLIC_ACCOUNT_TEMPLATE = + "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="; + /** + * 获取微信用户基本信息地址 + */ + public static final String WECHAT_OAUTH_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token"; + /** + * 获取ACCESS_TOKEN + */ + public static final String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token"; + /** + * 登录凭证校验 + */ + public static final String GET_OPEN_ID = "https://api.weixin.qq.com/sns/jscode2session"; + /** + * 获取小程序地址 + */ + public static final String GET_MINI_QRCODE = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s"; + /** + * 获取用户基本信息 + */ + public static final String GET_USER_BASIC_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN"; + /** + * 获取包含请求参数ACCESS_TOKEN + */ + public static final String ACCESS_TOKEN_WITH_PARAM = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; + /** + * 获取小程序 URL Link + */ + public static final String GENERATE_URL_LINK = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=%s"; + + public static final String WECHATPAY_SIGNATURE_TYPE = "Wechatpay-Signature-Type"; +} diff --git a/src/main/java/com/ycwl/basic/controller/FileController.java b/src/main/java/com/ycwl/basic/controller/FileController.java new file mode 100644 index 0000000..9b7c5ed --- /dev/null +++ b/src/main/java/com/ycwl/basic/controller/FileController.java @@ -0,0 +1,50 @@ +package com.ycwl.basic.controller; + + +import com.ycwl.basic.annotation.IgnoreToken; +import com.ycwl.basic.enums.BizCodeEnum; +import com.ycwl.basic.service.FileService; +import com.ycwl.basic.utils.ApiResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 文件接口 + * @Version: 1.0 + */ +@RestController +@RequestMapping("/api/file/v1") +@Slf4j +@Api(tags = "文件接口") +public class FileController { + + @Autowired + private FileService fileService; + + @ApiOperation(value = "上传文件") + @PostMapping("/upload") + @IgnoreToken + public ApiResponse upload(@RequestParam(value = "file") MultipartFile file) throws IOException { + String url = fileService.uploadFile(file); + return ApiResponse.success(url); + } + + @ApiOperation(value = "删除文件") + @PostMapping("/delete") + @IgnoreToken + public ApiResponse delete(@RequestParam(value = "fileName") String fileName) throws IOException { + Boolean flag = fileService.delete(fileName); + return flag ? ApiResponse.success(BizCodeEnum.REQUEST_OK) : ApiResponse.fail(BizCodeEnum.FAIL.getMessage()); + } +} diff --git a/src/main/java/com/ycwl/basic/controller/mobile/AppFaceController.java b/src/main/java/com/ycwl/basic/controller/mobile/AppFaceController.java index d0936e0..c75c5ea 100644 --- a/src/main/java/com/ycwl/basic/controller/mobile/AppFaceController.java +++ b/src/main/java/com/ycwl/basic/controller/mobile/AppFaceController.java @@ -14,7 +14,8 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/mobile/face/v1") @Api(tags = "用户人脸相关接口") -public class AppFaceController { +public class +AppFaceController { @ApiOperation("人脸有效性校验") diff --git a/src/main/java/com/ycwl/basic/controller/mobile/AppMemberController.java b/src/main/java/com/ycwl/basic/controller/mobile/AppMemberController.java index bcef0ab..7e9af00 100644 --- a/src/main/java/com/ycwl/basic/controller/mobile/AppMemberController.java +++ b/src/main/java/com/ycwl/basic/controller/mobile/AppMemberController.java @@ -1,12 +1,16 @@ package com.ycwl.basic.controller.mobile; +import com.ycwl.basic.annotation.IgnoreToken; +import com.ycwl.basic.model.mobile.DTO.WeChatUserInfoDTO; +import com.ycwl.basic.model.mobile.DTO.WeChatUserInfoUpdateDTO; +import com.ycwl.basic.model.pc.member.resp.MemberRespVO; +import com.ycwl.basic.service.mobile.AppMemberService; import com.ycwl.basic.utils.ApiResponse; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; /** * @Author:longbinbin @@ -15,30 +19,67 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/mobile/member/v1") @Api(tags = "用户相关接口") +@Slf4j public class AppMemberController { + @Autowired + private AppMemberService memberService; + + /** + * 登录 + * + * @param code + * @param userInfoDTO + * @return + * @throws Exception + */ + @ApiOperation("登录") @PostMapping("/login") - public ApiResponse login() { - //TODO 登录逻辑 - return ApiResponse.success(""); - } - - @PostMapping("/register") - public ApiResponse register() { - //TODO 注册逻辑 - return ApiResponse.success(""); + @IgnoreToken + public ApiResponse login(@RequestParam(value = "code", required = false) String code, + @RequestParam(value = "userInfoDTO", required = false) WeChatUserInfoDTO userInfoDTO) throws Exception { + return memberService.login(code, userInfoDTO); } + /** + * 获取用户信息 + * + * @return + */ + @ApiOperation("获取用户信息") @GetMapping("/getUserInfo") - public ApiResponse getUserInfo() { - //TODO 获取用户信息逻辑 - return ApiResponse.success(""); + public ApiResponse getUserInfo() { + return memberService.getUserInfo(); } + /** + * 修改用户信息 + * + * @param userInfoUpdateDTO + * @return + */ + @ApiOperation("修改用户信息") + @PostMapping("/update") + public ApiResponse update(@RequestBody WeChatUserInfoUpdateDTO userInfoUpdateDTO) { + return memberService.update(userInfoUpdateDTO); + } + + /** + * 同意用户协议 + * + * @return + */ + @ApiOperation("同意用户协议") + @GetMapping("/agreement") + public ApiResponse agreement() { + return memberService.agreement(); + } + + @ApiOperation("是否首次获取视频") @GetMapping("/isFirstObtainVideo") public ApiResponse isFirstTimeObtainingVideo() { - //TODO 判断是否首次获取视频逻辑 + // TODO 判断是否首次获取视频逻辑 return ApiResponse.success(""); } diff --git a/src/main/java/com/ycwl/basic/controller/mobile/AppWxPayController.java b/src/main/java/com/ycwl/basic/controller/mobile/AppWxPayController.java new file mode 100644 index 0000000..e9d6d50 --- /dev/null +++ b/src/main/java/com/ycwl/basic/controller/mobile/AppWxPayController.java @@ -0,0 +1,48 @@ +package com.ycwl.basic.controller.mobile; + + +import com.ycwl.basic.annotation.IgnoreToken; +import com.ycwl.basic.enums.BizCodeEnum; +import com.ycwl.basic.model.wxPay.WXPayOrderReqVO; +import com.ycwl.basic.model.wxPay.WxPayRespVO; +import com.ycwl.basic.service.mobile.WxPayService; +import com.ycwl.basic.utils.ApiResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 微信支付 + * @Version: 1.0 + */ +@RestController +@RequestMapping("/api/mobile/wx/v1") +@Api(tags = "微信支付相关接口") +public class AppWxPayController { + + @Autowired + private WxPayService wxPayService; + + @ApiOperation(value = "微信预支付", notes = "微信预支付") + @PostMapping("/createOrder") + public ApiResponse createOrder(@RequestBody @Validated WXPayOrderReqVO req) throws Exception { + return ApiResponse.success(wxPayService.createOrder(req)); + } + + @ApiOperation(value = "微信支付回调", notes = "微信支付回调") + @PostMapping("/payNotify") + @IgnoreToken + public ApiResponse payNotify(HttpServletRequest request) { + wxPayService.payNotify(request); + return ApiResponse.success(BizCodeEnum.REQUEST_OK); + } +} diff --git a/src/main/java/com/ycwl/basic/enums/AgreementEnum.java b/src/main/java/com/ycwl/basic/enums/AgreementEnum.java new file mode 100644 index 0000000..3aef854 --- /dev/null +++ b/src/main/java/com/ycwl/basic/enums/AgreementEnum.java @@ -0,0 +1,20 @@ +package com.ycwl.basic.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * 是否同意用户协议枚举 + * + * @author songmingsong + */ +@Getter +@NoArgsConstructor +@AllArgsConstructor +public enum AgreementEnum { + AGREE(1, "同意"), + NOT_AGREE(0, "未同意"); + private int type; + private String remark; +} diff --git a/src/main/java/com/ycwl/basic/enums/AppStatesCodeEnum.java b/src/main/java/com/ycwl/basic/enums/AppStatesCodeEnum.java deleted file mode 100644 index 1590eca..0000000 --- a/src/main/java/com/ycwl/basic/enums/AppStatesCodeEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.ycwl.basic.enums; - -import lombok.Getter; -import lombok.Setter; - -/** - * @author songmingsong - * @since 2022-11-23 - * 状态码定义 - */ -public enum AppStatesCodeEnum { - - /** - * 通用操作码 - */ - USER_STATES_CODE(1), - UNKNOWN_MISTAKE(500, "未知错误"), - NO_STAFFINFO_ERROR(411, "员工信息不存在"), - ; - - @Getter - public int code; - - @Getter - @Setter - public String message; - - AppStatesCodeEnum(int code, String message) { - this.code = code; - this.message = message; - } - - AppStatesCodeEnum(Integer statesCode) { - this.code = statesCode; - } -} diff --git a/src/main/java/com/ycwl/basic/enums/BizCodeEnum.java b/src/main/java/com/ycwl/basic/enums/BizCodeEnum.java index 28e2c99..0c303d7 100644 --- a/src/main/java/com/ycwl/basic/enums/BizCodeEnum.java +++ b/src/main/java/com/ycwl/basic/enums/BizCodeEnum.java @@ -1,41 +1,437 @@ package com.ycwl.basic.enums; import lombok.Getter; -import lombok.Setter; /** - * @author wenshijia - * @date 2021年05月25日 22:29 - * 状态码定义约束,共6位数,前三位代表服务,后4位代表接口 - * 比如 商品服务210,购物车是220、用户服务230,403代表权限 + * @author songmingsong + * @since 2022-11-23 + * 状态码定义 */ +@Getter public enum BizCodeEnum { + + /* -------------------------------------*/ + /* */ + /* 0:成功 1:失败 */ + /* */ + /* -------------------------------------*/ + /** - * 通用操作码 + * 成功 */ - UNKNOWN_MISTAKE(500, "未知错误"), + SUCCESS(0, "成功"), - VERIFY_CODE_ERROR(600, "验证码错误"), + /** + * 失败 + */ + FAIL(1, "失败"), + /** + * 异常 + */ + ERROR(-1, "异常"), - NO_VERIFY_CODE_AUTH(700, "未进行授权"), + /* -------------------------------------*/ + /* */ + /* 200-550 服务器相关 */ + /* */ + /* -------------------------------------*/ + /** + * 无效请求 + */ + REQUEST_OK(200, "请求成功"), + /** + * 无效请求 + */ + BAD_REQUEST(400, "无效请求"), - TOLL_MACHINE_BIND_APPLICATION_SITE(1000, "当前收费机已经绑定了应用场所"), - TOLL_MACHINE_EXIST(1001, "当前收费机编号已经存在"), - APPLICATION_SITE_EXIST(1002, "当前应用场所已经存在"), + /** + * 参数错误 + */ + PARAM_ERROR(400, "参数错误,缺少必要参数"), - CONFERENCE_DEVICE_EXIST(2001, "当前会议设设备已经存在"), + /** + * 无效鉴权信息 + */ + UNAUTHORIZED(401, "无效授权信息"), + + /** + * 登陆失效 + */ + TOKEN_INVALID(401, "登录失效,请重新登录"), + + /** + * 无权访问此资源 + */ + FORBIDDEN(403, "无权访问此资源"), + + /** + * 无效的访问地址 + */ + INVALID_URL(404, "无效的访问地址"), + + /** + * 不支持的请求方式 + */ + NOT_SUPPORTED(405, "不支持的请求方式"), + + /** + * 网络异常 + */ + REQUEST_TIMEOUT(408, "请求超时"), + + /** + * 数据完整性异常 数据过长 过短 + */ + LENGTH_REQUIRED(411, "数据完整性异常"), + /** + * 数据约束性异常 + */ + CONSTRAINT_EXCEPTION(412, "重复提交或者数据已存在"), + + + /** + * 数据格式错误 + */ + DATA_FORMAT_ERROR(415, "数据格式错误"), + /** + * 此地址暂不可使用 + */ + METHOD_FAILURE(420, "此地址暂不可使用"), + /** + * 账户已锁定 + */ + LOCKED(423, "账户已锁定,请联系客服"), + + /** + * 请求过于频繁 + */ + TOO_MANY_REQUESTS(429, "请求过于频繁"), + /** + * 远程访问异常 + */ + REMOTE_ACCESS_EXCEPTION(430, " 访问远程主机时发生了异常"), + /** + * 错误的服务API + */ + ERROR_SERVER_API(431, "错误的服务API"), + /** + * 错误的签名 + */ + ERROR_SIGN_ACCESS(432, "Access Sign Error"), + + + /** + * 无效鉴权信息 + */ + INVALID_REFRESH(433, "无效刷新信息"), + + /** + * 服务器内部错误 + */ + SERVER_INTERNAL_ERROR(500, "服务器内部错误"), + + /** + * 未知错误 + */ + SERVER_UNKONWN_ERROR(500, "未知错误"), + + + /** + * 暂不可服务 + */ + SERVICE_UNAVAILABLE(503, "暂不可服务"), + /** + * 需要认证 + */ + AUTHENTICATION_REQUIRED(511, "需要认证"), + + /** + * 无数据 + */ + NO_DATA(512, "无数据"), + /** + * 存在关联数据 + */ + DATA_RELATION(513, "存在关联数据"), + /** + * 该数据已填写 + */ + DATA_COMPLETED(514, "该数据已填写"), + /* -------------------------------------*/ + /* */ + /* 600-619 登录相关业务码 */ + /* */ + /* -------------------------------------*/ + + /** + * 账号不存在 + */ + ACCOUNT_NON_EXISTENT(601, "账号不存在"), + + /** + * 账号已存在 + */ + ACCOUNT_EXISTENT(602, "账号已存在"), + + /** + * 账户密码错误 + */ + ACCOUNT_PASSWORD_ERROR(603, "用户名或密码不正确"), + + /** + * 绵阳授权码错误 + */ + MYang_AUTHORIZATION_CODE_ERROR(6031, "授权码不正确"), + + /** + * 手机号已绑定 + */ + TEL_IS_BIND(604, "手机号已绑定"), + + /** + * 未绑定手机号 + */ + TEL_IS_NOT_BIND(605, "尚未绑定手机号,请先绑定手机号"), + + /** + * 无效的登录类型 + */ + ERROR_LOGIN_CHANNEL(606, "无效的登录类型"), + /** + * token已过期 + */ + TOKEN_EXPIRED(608, "token已过期"), + + /** + * 账户在其他地方登录 + */ + ACCOUNTS_IS_LOGGED_IN_ELSEWHERE(609, "登录失效,账号已在其他设备登录。"), + + /** + * 第三方登录失败 + */ + THIRD_LOGIN_ERROR(610, "第三方登录失败"), + + /** + * 不支持的登录方式 + */ + NOT_SUPPORT_LOGIN_CHANNEL(611, "暂不支持的登录方式"), + + /** + * 需要登录后操作 + */ + NEED_LOGIN(612, "需要登录后操作"), + + /** + * 尚未设置密码 + */ + LOGIN_PASSWORD_HAS_NOT_BEEN_SET(613, "账号或密码错误,请重新输入"), + + /** + * 注册失败 + */ + REGISTER_FAIL(614, "注册失败"), + + /** + * 连续登录错误达到阈值,请稍后再试 + */ + CONTINUOUS_LOGIN_ERROR(615, "错误密码输入次数过多,请%s分钟后再试"), + + APP_CONTINUOUS_LOGIN_ERROR(616, "错误密码输入次数过多,请%s分钟后再试"), + + /** + * refreshToken已过期 + */ + REFRESH_TOKEN_EXPIRE(618, "refreshToken已过期"), + + CHANNEL_ERROR(619, "渠道错误"), + + + /* -------------------------------------*/ + /* */ + /* 620-639 验证码相关业务码 */ + /* */ + /* -------------------------------------*/ + + /** + * 验证码获取失败 + */ + VERIFY_CODE_FAIL(621, "验证码获取失败"), + + /** + * 无效验证码 + */ + INVALID_VERIFY_CODE(622, "无效验证码"), + + /** + * 验证码已使用 + */ + USED_VERIFY_CODE(623, "验证码已使用"), + + /** + * 验证码已过期 + */ + EXPIRE_VERIFY_CODE(624, "验证码已失效,请重新输入"), + + /** + * 没有输入验证码 + */ + NOT_HAS_VERIFY_CODE(625, "没有输入验证码"), + + /** + * 错误的验证码 + */ + ERROR_VERIFY_CODE(626, "验证码错误,请重新输入"), + + + /* -------------------------------------*/ + /* */ + /* 640-659 角色相关业务码 */ + /* */ + /* -------------------------------------*/ + + /** + * 角色已存在 + */ + ROLE_EXIST(640, "角色已存在"), + + /** + * 角色不存在 + */ + ROLE_NOT_EXIST(641, "角色不存在"), + + HAS_NOT_ROLE(642, "当前登录人没有角色"), + + + /* -------------------------------------*/ + /* */ + /* 660-679 文件相关业务码 */ + /* */ + /* -------------------------------------*/ + + /** + * 文件过大 + */ + FILE_TOO_LARGE(660, "文件过大"), + + /** + * 不支持的文件类型 + */ + UNSUPPORTED_FILE_TYPE(661, "不支持的文件类型"), + + /** + * 文件不存在 + */ + FILE_NOT_EXIST(662, "文件不存在"), + /** + * 文件上传失败 + */ + UPLOAD_FAILED(663, "文件上传失败"), + + /* -------------------------------------*/ + /* */ + /* 7** 其他业务码 */ + /* */ + /* -------------------------------------*/ + + /** + * 开始时间不能大于结束时间 + */ + TIME_FAIL(704, "%s开始时间需小于结束时间"), + + /** + * 二维码获取失败 + */ + QR_CODE_GET_FAIL(705, "二维码获取失败"), + + /** + * 有尚未完成的任务 + */ + TASK_IS_NOT_OVER(706, "有尚未完成的任务"), + + /** + * 该记录已经审核 + */ + REMARK_IS_EXAMINE(707, "该记录已经审核"), + + /** + * 只有管理员才能操作 + */ + SHOP_ONLY_MANAGER_HANDLE(708, "只有管理员才能操作"), + + /** + * 群主不能被删除 + */ + EMCHAT_GROUP_DELETE_ERROR(709, "群主不能被删除"), + + /** + * 存在重复数据 + */ + EMCHAT_REPEAT_ERROR(710, "存在重复数据"), + + /** + * 姓名或手机号不能为空 + */ + NAME_PHONE_NOT_NULL(801, "姓名或手机号不能为空"), + + /** + * 该手机号用户已存在 + */ + PHONE_EXIST(802, "该手机号用户已存在"), + + /** + * 导出失败 + */ + EXPORT_FAIL(804, "导出失败"), + + /** + * 当前资源路径已被占用 + */ + PATH_ALREADY_EXIST(805, "当前资源路径已被占用"), + + /** + * 功能不能使用已有的菜单路径 + */ + FUNCTION_NOT_MAKE_MENU(806, "功能不能使用已有的菜单路径"), + + /** + * 内置角色不能删除 + */ + BUILT_IN_ROLE_NOT_DELETE(807, "内置角色不能删除"), + + + /* -------------------------------------*/ + /* */ + /* 901-999 角色相关业务码 */ + /* */ + /* -------------------------------------*/ + USER_ORGANIZATION(901, "该手机号不属于有效账号,请重新输入"), + + + /* -------------------------------------*/ + /* */ + /* 1000-1019 账号密码相关业务码 */ + /* */ + /* -------------------------------------*/ + OLD_PSW_ERROR(1000, "原密码错误,请重新输入"), + + /* -------------------------------------*/ + /* */ + /* 2000-2019 微信支付相关业务码 */ + /* */ + /* -------------------------------------*/ + ADVANCE_PAYMENT_FAILED(2001, "预支付失败"), + ADVANCE_PAYMENT_CALLBACK_FAILED(2002, "预支付回调失败"), ; - @Getter - public int code; - @Getter - @Setter - public String message; - - BizCodeEnum(int code, String message) { + BizCodeEnum(Integer code, String message) { this.code = code; this.message = message; } + + private Integer code; + + private String message; } diff --git a/src/main/java/com/ycwl/basic/enums/OrderStateEnum.java b/src/main/java/com/ycwl/basic/enums/OrderStateEnum.java new file mode 100644 index 0000000..729f999 --- /dev/null +++ b/src/main/java/com/ycwl/basic/enums/OrderStateEnum.java @@ -0,0 +1,25 @@ +package com.ycwl.basic.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * 订单状态枚举 + * + * @author songmingsong + */ +@Getter +@NoArgsConstructor +@AllArgsConstructor +public enum OrderStateEnum { + NOT_PRESENTED(0, "未提出",1), + PASSED(1, "已通过",1), + UNPAID(0, "未支付",2), + PAID(1, "已支付",2), + REFUNDED(2, "已退款",2), + CANCELED(9, "已取消",2); + private int state; + private String remark; + private int type; // 1-退款 2-支付 +} diff --git a/src/main/java/com/ycwl/basic/enums/WechatErrorCodeEnum.java b/src/main/java/com/ycwl/basic/enums/WechatErrorCodeEnum.java new file mode 100644 index 0000000..8765263 --- /dev/null +++ b/src/main/java/com/ycwl/basic/enums/WechatErrorCodeEnum.java @@ -0,0 +1,47 @@ +package com.ycwl.basic.enums; + +import lombok.Getter; + +/** + *

@AUTHOR: songmingsong

+ */ +@Getter +public enum WechatErrorCodeEnum { + /** + * 无效的预登陆code + */ + INVALID_CODE("40029", "无效的预登陆code"), + + /** + * 预登陆code已经使用 + */ + CODE_IS_USED("40163", "预登陆code已经使用"), + + /** + * 无效的AppSecret + */ + INVALID_APP_SECRET("40125", "无效的AppSecret"), + + /** + * 未知的微信错误 + */ + UNKNOWN_ERROR_CODE("50000", "未知的微信错误"); + + WechatErrorCodeEnum(String code, String detail) { + this.code = code; + this.detail = detail; + } + + private String code; + + private String detail; + + public static WechatErrorCodeEnum getErrorCode(String code) { + for (WechatErrorCodeEnum wechatErrorCodeEnum : WechatErrorCodeEnum.values()) { + if (wechatErrorCodeEnum.code.equals(code)) { + return wechatErrorCodeEnum; + } + } + return UNKNOWN_ERROR_CODE; + } +} diff --git a/src/main/java/com/ycwl/basic/exception/AppException.java b/src/main/java/com/ycwl/basic/exception/AppException.java index ae77c6c..f3cfb39 100644 --- a/src/main/java/com/ycwl/basic/exception/AppException.java +++ b/src/main/java/com/ycwl/basic/exception/AppException.java @@ -1,6 +1,6 @@ package com.ycwl.basic.exception; -import com.ycwl.basic.enums.AppStatesCodeEnum; +import com.ycwl.basic.enums.BizCodeEnum; import lombok.Data; /** @@ -19,9 +19,14 @@ public class AppException extends RuntimeException { this.msg = message; } - public AppException(AppStatesCodeEnum mobileCodeEnum) { + public AppException(BizCodeEnum mobileCodeEnum) { super(mobileCodeEnum.getMessage()); this.code = mobileCodeEnum.getCode(); this.msg = mobileCodeEnum.getMessage(); } + + public AppException(BizCodeEnum mobileCodeEnum, String msg) { + this.code = mobileCodeEnum.getCode(); + this.msg = msg; + } } diff --git a/src/main/java/com/ycwl/basic/exception/CustomExceptionHandle.java b/src/main/java/com/ycwl/basic/exception/CustomExceptionHandle.java index 47aac5a..9e21f55 100644 --- a/src/main/java/com/ycwl/basic/exception/CustomExceptionHandle.java +++ b/src/main/java/com/ycwl/basic/exception/CustomExceptionHandle.java @@ -59,7 +59,7 @@ public class CustomExceptionHandle { @ExceptionHandler(value = Exception.class) public ApiResponse handle(Exception e) { LOGGER.error("系统异常 -> {}", e.getMessage(), e); - return ApiResponse.buildResult(BizCodeEnum.UNKNOWN_MISTAKE); + return ApiResponse.buildResult(BizCodeEnum.SERVER_UNKONWN_ERROR); } /** diff --git a/src/main/java/com/ycwl/basic/model/jwt/JwtInfo.java b/src/main/java/com/ycwl/basic/model/jwt/JwtInfo.java index f3adc48..773a0cb 100644 --- a/src/main/java/com/ycwl/basic/model/jwt/JwtInfo.java +++ b/src/main/java/com/ycwl/basic/model/jwt/JwtInfo.java @@ -39,6 +39,7 @@ public class JwtInfo implements Serializable { * 用户账号 */ private String account; + private String phone; diff --git a/src/main/java/com/ycwl/basic/model/mobile/DTO/WeChatUserInfoDTO.java b/src/main/java/com/ycwl/basic/model/mobile/DTO/WeChatUserInfoDTO.java new file mode 100644 index 0000000..57c7fa9 --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/mobile/DTO/WeChatUserInfoDTO.java @@ -0,0 +1,42 @@ +package com.ycwl.basic.model.mobile.DTO; + + +import lombok.Data; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 微信用户信息 + * @Version: 1.0 + */ +@Data +public class WeChatUserInfoDTO { + /** + * 头像 + */ + private String avatarUrl; + /** + * 微信昵称 + */ + private String nickname; + /** + * 是否同意用户协议,1同意0未同意 + */ + private Integer agreement; + /** + * 电话号码 + */ + private String phone; + /** + * 国家 + */ + private String country; + /** + * 省份 + */ + private String province; + /** + * 城市 + */ + private String city; +} diff --git a/src/main/java/com/ycwl/basic/model/mobile/DTO/WeChatUserInfoUpdateDTO.java b/src/main/java/com/ycwl/basic/model/mobile/DTO/WeChatUserInfoUpdateDTO.java new file mode 100644 index 0000000..2995e5e --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/mobile/DTO/WeChatUserInfoUpdateDTO.java @@ -0,0 +1,26 @@ +package com.ycwl.basic.model.mobile.DTO; + + +import lombok.Data; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 修改微信用户信息 + * @Version: 1.0 + */ +@Data +public class WeChatUserInfoUpdateDTO { + /** + * 头像 + */ + private String avatarUrl; + /** + * 微信昵称 + */ + private String nickname; + /** + * 是否同意用户协议,1同意0未同意 + */ + private Long id; +} diff --git a/src/main/java/com/ycwl/basic/model/mobile/WeChatUserInfoModel.java b/src/main/java/com/ycwl/basic/model/mobile/WeChatUserInfoModel.java new file mode 100644 index 0000000..783c893 --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/mobile/WeChatUserInfoModel.java @@ -0,0 +1,34 @@ +package com.ycwl.basic.model.mobile; + + +import lombok.Data; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 微信用户信息 + * @Version: 1.0 + */ +@Data +public class WeChatUserInfoModel { + /** + * 微信openid + */ + private String openId; + /** + * 用户id + */ + private String id; + /** + * 头像 + */ + private String avatarUrl; + /** + * 微信昵称 + */ + private String nickname; + /** + * 真实名称 + */ + private String realName; +} diff --git a/src/main/java/com/ycwl/basic/model/pc/member/entity/MemberEntity.java b/src/main/java/com/ycwl/basic/model/pc/member/entity/MemberEntity.java index dff4f19..1c1d600 100644 --- a/src/main/java/com/ycwl/basic/model/pc/member/entity/MemberEntity.java +++ b/src/main/java/com/ycwl/basic/model/pc/member/entity/MemberEntity.java @@ -23,6 +23,10 @@ public class MemberEntity { * 微信昵称 */ private String nickname; + /** + * 微信头像 + */ + private String avatarUrl; /** * 真实姓名 */ diff --git a/src/main/java/com/ycwl/basic/model/wxPay/WXPayOrderReqVO.java b/src/main/java/com/ycwl/basic/model/wxPay/WXPayOrderReqVO.java new file mode 100644 index 0000000..d4e0e63 --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/wxPay/WXPayOrderReqVO.java @@ -0,0 +1,29 @@ +package com.ycwl.basic.model.wxPay; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 预支付请求类 + * @Version: 1.0 + */ +@Data +@Accessors(chain = true) +public class WXPayOrderReqVO { + + @ApiModelProperty(value = "总金额(单位:分)",required = true) + private Integer totalPrice; + + @ApiModelProperty(value = "商品名称",required = true) + private String goodsName; + + @ApiModelProperty(value = "openid",required = true) + private String openId; + + @ApiModelProperty(value = "商品订单号",required = true) + private Long orderSn; +} diff --git a/src/main/java/com/ycwl/basic/model/wxPay/WxPayRespVO.java b/src/main/java/com/ycwl/basic/model/wxPay/WxPayRespVO.java new file mode 100644 index 0000000..448cffd --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/wxPay/WxPayRespVO.java @@ -0,0 +1,41 @@ +package com.ycwl.basic.model.wxPay; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 支付返回类 + * @Version: 1.0 + */ +@Data +@Accessors(chain = true) +public class WxPayRespVO implements Serializable { + + private static final long serialVersionUID = 1L; + /** + * 预支付交易会话标识小程序下单接口返回的prepay_id参数值 + */ + @ApiModelProperty("预支付交易会话标识小程序下单接口返回的prepay_id参数值") + private String prepayId; + /** + * 随机字符串 + */ + @ApiModelProperty("随机字符串") + private String nonceStr; + /** + * 时间戳 + */ + @ApiModelProperty("时间戳") + private Long timeStamp; + /** + * 签名 + */ + @ApiModelProperty("签名") + private String paySign; +} diff --git a/src/main/java/com/ycwl/basic/model/wxPay/WxchatCallbackSuccessData.java b/src/main/java/com/ycwl/basic/model/wxPay/WxchatCallbackSuccessData.java new file mode 100644 index 0000000..cefbf97 --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/wxPay/WxchatCallbackSuccessData.java @@ -0,0 +1,69 @@ +package com.ycwl.basic.model.wxPay; + + +import cn.hutool.core.date.DateUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 微信订单查询结果 + * @Version: 1.0 + */ +@Data +@Slf4j +public class WxchatCallbackSuccessData { + + /** + * 商户订单号 + */ + private String orderId; + + + /** + * 交易状态 + * SUCCESS:支付成功 + * REFUND:转入退款 + * NOTPAY:未支付 + * CLOSED:已关闭 + * REVOKED:已撤销(付款码支付) + * USERPAYING:用户支付中(付款码支付) + * PAYERROR:支付失败(其他原因,如银行返回失败) + */ + private String tradestate; + + /** + * 支付完成时间 + */ + private Date successTime; + + /** + * 交易类型 + * JSAPI:公众号支付 + * NATIVE:扫码支付 + * APP:APP支付 + * MICROPAY:付款码支付 + * MWEB:H5支付 + * FACEPAY:刷脸支付 + */ + private String tradetype; + + /** + * 订单总金额 + */ + private BigDecimal totalMoney; + + + public Date getSuccessTime() { + return successTime; + } + + public void setSuccessTime(String successTime) { + // Hutool工具包的方法,自动识别一些常用格式的日期字符串 + this.successTime = DateUtil.parse(successTime); + } +} diff --git a/src/main/java/com/ycwl/basic/service/FileService.java b/src/main/java/com/ycwl/basic/service/FileService.java new file mode 100644 index 0000000..6bfcb89 --- /dev/null +++ b/src/main/java/com/ycwl/basic/service/FileService.java @@ -0,0 +1,29 @@ +package com.ycwl.basic.service; + +import com.ycwl.basic.utils.OssUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Objects; + +/** + * file请求服务 + * + * @author songmingsong + */ +@Service +public class FileService { + + @Autowired + private OssUtil ossUtil; + + public String uploadFile(MultipartFile file) throws IOException { + return ossUtil.uploadFile(file.getInputStream(), Objects.requireNonNull(file.getOriginalFilename())); + } + + public Boolean delete(String fileName) { + return ossUtil.deleteFile(fileName); + } +} diff --git a/src/main/java/com/ycwl/basic/service/HttpService.java b/src/main/java/com/ycwl/basic/service/HttpService.java new file mode 100644 index 0000000..7f5da2d --- /dev/null +++ b/src/main/java/com/ycwl/basic/service/HttpService.java @@ -0,0 +1,121 @@ +package com.ycwl.basic.service; + +import com.ycwl.basic.utils.HttpServiceUtil; +import com.ycwl.basic.utils.SslUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpEntity; +import org.apache.http.NameValuePair; +import org.apache.http.StatusLine; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Http请求服务 + * + * @author songmingsong + */ +@Service +@Slf4j +public class HttpService { + + /** + * @param requestUrl + * 请求地址 + * @param params + * 参数 + * @param encoding + * 编码 + * @return String + * @throws Exception + * 抛出的异常新 + */ + public String doHttpsPost(String requestUrl, Map params, String encoding) throws Exception { + String result = HttpServiceUtil.REQUEST_NO_RESULT; + // build client 对象 + CloseableHttpClient httpClient = null; + try { + httpClient = SslUtil.sslHttpClientBuild(); + requestUrl = requestUrl.replaceAll("\\s*", ""); + HttpPost post = new HttpPost(requestUrl); + RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000) + .setConnectionRequestTimeout(1000).setSocketTimeout(5000).build(); + post.setConfig(requestConfig); + // 请求首部--可选的,User-Agent对于一些服务器必选,不加可能不会返回正确结果 + post.setHeader("User-Agent", + "Mozilla/5.0 (Linux; U; Android 5.0.2; zh-cn; PLK-UL00 Build/HONORPLK-UL00) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.4 TBS/025483 Mobile Safari/533.1 MicroMessenger/6.3.8.56_re6b2553.680 NetType/"); + List parasList = new ArrayList(); + if (null != params && params.size() > 0) { + for (String key : params.keySet()) { + NameValuePair param = new BasicNameValuePair(key, params.get(key)); + parasList.add(param); + } + } + // Exec Request + HttpEntity paramsEntity = new UrlEncodedFormEntity(parasList, encoding); + post.setEntity(paramsEntity); + CloseableHttpResponse resp = httpClient.execute(post); + + // 获得起始行 + StatusLine status = resp.getStatusLine(); + // 获取实体 + if (HttpServiceUtil.success(status)) { + // 获取实体 + HttpEntity entity = resp.getEntity(); + // 编码格式 + result = EntityUtils.toString(entity, encoding); + // 释放实体 response.close();//关闭响应 + EntityUtils.consume(entity); + } + } catch (Exception e) { + log.error("doHttpsPost", e); + throw e; + } finally { + if (httpClient != null) { + httpClient.close(); + } + } + return result; + + } + + public String doGet(String requestUrl, String encoding) throws Exception { + String result = HttpServiceUtil.REQUEST_NO_RESULT; + // build client 对象 + CloseableHttpClient httpClient = null; + try { + httpClient = SslUtil.sslHttpClientBuild(); + requestUrl = requestUrl.replaceAll("\\s*", ""); + HttpGet get = new HttpGet(requestUrl); + // 请求首部--可选的,User-Agent对于一些服务器必选,不加可能不会返回正确结果 + get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64;rv:39.0) Gecko/20100101 Firefox/39.0"); + // Exec Request + CloseableHttpResponse resp = httpClient.execute(get); + // 获得起始行 + StatusLine status = resp.getStatusLine(); + if (HttpServiceUtil.success(status)) { + // 获取实体 + HttpEntity entity = resp.getEntity(); + // 编码格式 + result = EntityUtils.toString(entity, encoding); + // 释放实体 response.close();//关闭响应 + EntityUtils.consume(entity); + } + } finally { + if (httpClient != null) { + httpClient.close(); + } + } + return result; + } +} diff --git a/src/main/java/com/ycwl/basic/service/impl/mobile/AppMemberServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/mobile/AppMemberServiceImpl.java new file mode 100644 index 0000000..bbc52b6 --- /dev/null +++ b/src/main/java/com/ycwl/basic/service/impl/mobile/AppMemberServiceImpl.java @@ -0,0 +1,141 @@ +package com.ycwl.basic.service.impl.mobile; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ycwl.basic.config.WechatConfig; +import com.ycwl.basic.constant.BaseContextHandler; +import com.ycwl.basic.constant.NumberConstant; +import com.ycwl.basic.constant.WeiXinConstant; +import com.ycwl.basic.enums.AgreementEnum; +import com.ycwl.basic.enums.BizCodeEnum; +import com.ycwl.basic.enums.WechatErrorCodeEnum; +import com.ycwl.basic.exception.AppException; +import com.ycwl.basic.mapper.pc.MemberMapper; +import com.ycwl.basic.model.jwt.JwtInfo; +import com.ycwl.basic.model.mobile.DTO.WeChatUserInfoDTO; +import com.ycwl.basic.model.mobile.DTO.WeChatUserInfoUpdateDTO; +import com.ycwl.basic.model.pc.member.entity.MemberEntity; +import com.ycwl.basic.model.pc.member.req.MemberReqQuery; +import com.ycwl.basic.model.pc.member.resp.MemberRespVO; +import com.ycwl.basic.service.HttpService; +import com.ycwl.basic.service.mobile.AppMemberService; +import com.ycwl.basic.utils.ApiResponse; +import com.ycwl.basic.utils.BeanCopierUtils; +import com.ycwl.basic.utils.JwtTokenUtil; +import com.ycwl.basic.utils.SnowFlakeUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Author:songmingsong + */ +@Slf4j +@Service +public class AppMemberServiceImpl implements AppMemberService { + @Autowired + private WechatConfig config; + + @Autowired + private HttpService httpService; + + @Autowired + private MemberMapper memberMapper; + + @Autowired + private JwtTokenUtil jwtTokenUtil; + + @Override + public Map getOpenId(String code) { + Map paramMap = new HashMap<>(NumberConstant.FOUR); + paramMap.put("appid", config.getMiniProgramAppId()); + paramMap.put("secret", config.getMiniProgramSecret()); + paramMap.put("js_code", code); + paramMap.put("grant_type", config.getGrandType()); + try { + String response = httpService.doHttpsPost(WeiXinConstant.GET_OPEN_ID, paramMap, "UTF-8"); + if (StringUtils.isBlank(response)) { + return null; + } + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(response, Map.class); + } catch (Exception e) { + log.error("getOpenId", e); + throw new AppException(BizCodeEnum.SERVER_INTERNAL_ERROR); + } + } + + + @Override + public ApiResponse login(String code, WeChatUserInfoDTO userInfoDTO) throws Exception { + Map weixinResponse = this.getOpenId(code); + if (CollectionUtils.isEmpty(weixinResponse)) { + throw new AppException(BizCodeEnum.SERVER_INTERNAL_ERROR); + } + Object errcode = weixinResponse.get("errcode"); + if (errcode != null) { + WechatErrorCodeEnum errorCode = WechatErrorCodeEnum.getErrorCode(errcode.toString()); + throw new AppException(BizCodeEnum.PARAM_ERROR.getCode(), errorCode.getDetail()); + } + + // 获取openId + Object openId = weixinResponse.get("openid"); + if (openId == null) { + log.warn("warning={}", "未获取到当前用户openId"); + throw new AppException(BizCodeEnum.SERVER_UNKONWN_ERROR, "未获取到当前用户openId"); + } + + MemberRespVO memberRespVO = new MemberRespVO(); + JwtInfo jwtInfo = new JwtInfo(); + // 根据返回的openId,判断用户是否是新用户,是的话,将用户信息存到数据库; + MemberReqQuery memberReqQuery = new MemberReqQuery(); + memberReqQuery.setOpenId(openId.toString()); + List list = memberMapper.list(memberReqQuery); + if (list.isEmpty()) { + MemberEntity memberEntity = new MemberEntity(); + memberEntity.setId(SnowFlakeUtil.getLongId()); + BeanCopierUtils.copyProperties(userInfoDTO, memberEntity); + memberMapper.add(memberEntity); + BeanCopierUtils.copyProperties(memberEntity, memberRespVO); + } else { + BeanCopierUtils.copyProperties(list.get(NumberConstant.ZERO), memberRespVO); + } + jwtInfo.setUserId(memberRespVO.getId().toString()); + jwtInfo.setName(memberRespVO.getNickname()); + jwtInfo.setPhone(memberRespVO.getPhone()); + + String jwt = jwtTokenUtil.generateToken(jwtInfo); + Map resMap = new HashMap<>(); + resMap.put("token", jwt); + resMap.put("user", memberRespVO); + + return ApiResponse.success(resMap); + } + + @Override + public ApiResponse getUserInfo() { + MemberRespVO respVO = memberMapper.getById(Long.parseLong(BaseContextHandler.getUserId())); + return ApiResponse.success(respVO); + } + + @Override + public ApiResponse update(WeChatUserInfoUpdateDTO userInfoUpdateDTO) { + MemberEntity memberEntity = new MemberEntity(); + memberEntity.setId(Long.parseLong(BaseContextHandler.getUserId())); + BeanCopierUtils.copyProperties(userInfoUpdateDTO, memberEntity); + return ApiResponse.success(memberMapper.update(memberEntity)); + } + + @Override + public ApiResponse agreement() { + MemberEntity memberEntity = new MemberEntity(); + memberEntity.setId(Long.parseLong(BaseContextHandler.getUserId())); + memberEntity.setAgreement(AgreementEnum.AGREE.getType()); + return ApiResponse.success(memberMapper.update(memberEntity)); + } +} diff --git a/src/main/java/com/ycwl/basic/service/impl/mobile/WxPayServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/mobile/WxPayServiceImpl.java new file mode 100644 index 0000000..63db52b --- /dev/null +++ b/src/main/java/com/ycwl/basic/service/impl/mobile/WxPayServiceImpl.java @@ -0,0 +1,201 @@ +package com.ycwl.basic.service.impl.mobile; + + +import com.alibaba.fastjson.JSONObject; +import com.wechat.pay.java.core.Config; +import com.wechat.pay.java.core.RSAAutoCertificateConfig; +import com.wechat.pay.java.core.exception.ServiceException; +import com.wechat.pay.java.core.notification.NotificationConfig; +import com.wechat.pay.java.core.notification.NotificationParser; +import com.wechat.pay.java.core.notification.RequestParam; +import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction; +import com.wechat.pay.java.service.payments.jsapi.JsapiService; +import com.wechat.pay.java.service.payments.jsapi.model.*; +import com.wechat.pay.java.service.payments.model.TransactionAmount; +import com.ycwl.basic.config.WechatConfig; +import com.ycwl.basic.constant.WeiXinConstant; +import com.ycwl.basic.enums.BizCodeEnum; +import com.ycwl.basic.enums.OrderStateEnum; +import com.ycwl.basic.exception.AppException; +import com.ycwl.basic.mapper.pc.OrderMapper; +import com.ycwl.basic.model.wxPay.WXPayOrderReqVO; +import com.ycwl.basic.model.wxPay.WxPayRespVO; +import com.ycwl.basic.model.wxPay.WxchatCallbackSuccessData; +import com.ycwl.basic.service.mobile.WxPayService; +import com.ycwl.basic.service.pc.OrderService; +import com.ycwl.basic.utils.WXPayUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.wechat.pay.java.core.http.Constant.*; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 微信支付实现 + * @Version: 1.0 + */ +@Slf4j +@Service +public class WxPayServiceImpl implements WxPayService { + + @Autowired + private WechatConfig wechatConfig; + @Autowired + private OrderMapper orderMapper; + @Autowired + private OrderService orderService; + + @Override + public WxPayRespVO createOrder(WXPayOrderReqVO req) { + try { + // 使用自动更新平台证书的RSA配置 + // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错 + Config config = + new RSAAutoCertificateConfig.Builder() + .merchantId(wechatConfig.getMchId()) + .privateKeyFromPath(wechatConfig.getKeyPath()) + .merchantSerialNumber(wechatConfig.getMchSerialNo()) + .apiV3Key(wechatConfig.getApiV3()) + .build(); + // 构建service + JsapiService service = new JsapiService.Builder().config(config).build(); + + // request.setXxx(val)设置所需参数,具体参数可见Request定义 + PrepayRequest request = new PrepayRequest(); + Amount amount = new Amount(); + amount.setTotal(req.getTotalPrice()); + request.setAmount(amount); + request.setAppid(wechatConfig.getAppId()); + request.setMchid(wechatConfig.getMchId()); + request.setDescription(req.getGoodsName()); + request.setNotifyUrl(wechatConfig.getNotifyUrl()); + request.setOutTradeNo(req.getOrderSn().toString()); + + Payer payer = new Payer(); + payer.setOpenid(req.getOpenId()); + request.setPayer(payer); + + // 调用下单方法,得到应答 + PrepayResponse response = service.prepay(request); + WxPayRespVO vo = new WxPayRespVO(); + Long timeStamp = System.currentTimeMillis() / 1000; + vo.setTimeStamp(timeStamp); + String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32); + vo.setNonceStr(substring); + String signatureStr = Stream.of(wechatConfig.getAppId(), String.valueOf(timeStamp), substring, "prepay_id=" + response.getPrepayId()) + .collect(Collectors.joining("\n", "", "\n")); + String sign = WXPayUtil.getSign(signatureStr, wechatConfig.getKeyPath()); + vo.setPaySign(sign); + vo.setPrepayId("prepay_id=" + response.getPrepayId()); + return vo; + } catch (ServiceException e) { + JSONObject parse = JSONObject.parseObject(e.getResponseBody()); + throw new AppException(BizCodeEnum.ADVANCE_PAYMENT_FAILED, parse.getString("message")); + } catch (Exception e) { + throw new AppException(BizCodeEnum.ADVANCE_PAYMENT_FAILED, e.toString()); + } + } + + @Override + public void payNotify(HttpServletRequest request) { + try { + // 读取请求体的信息 + ServletInputStream inputStream = request.getInputStream(); + StringBuffer stringBuffer = new StringBuffer(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String s; + // 读取回调请求体 + while ((s = bufferedReader.readLine()) != null) { + stringBuffer.append(s); + } + String s1 = stringBuffer.toString(); + String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP); + String nonce = request.getHeader(WECHAT_PAY_NONCE); + String signType = request.getHeader(WeiXinConstant.WECHATPAY_SIGNATURE_TYPE); + String serialNo = request.getHeader(WECHAT_PAY_SERIAL); + String signature = request.getHeader(WECHAT_PAY_SIGNATURE); + + NotificationConfig config = new RSAAutoCertificateConfig.Builder() + .merchantId(wechatConfig.getMchId()) + .privateKeyFromPath(wechatConfig.getKeyPath()) + .merchantSerialNumber(wechatConfig.getMchSerialNo()) + .apiV3Key(wechatConfig.getApiV3()) + .build(); + // 初始化 NotificationParser + NotificationParser parser = new NotificationParser(config); + RequestParam requestParam = new RequestParam.Builder() + .serialNumber(serialNo) + .nonce(nonce) + .signature(signature) + .timestamp(timestamp) + // 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048 + .signType(signType) + .body(s1) + .build(); + Transaction parse = parser.parse(requestParam, Transaction.class); + log.info("[微信支付]parse = {}", parse); + + // 更新订单信息 + new Thread(() -> { + Transaction.TradeStateEnum tradeState = parse.getTradeState(); + OrderStateEnum OrderStateEnum = null; + if (Transaction.TradeStateEnum.SUCCESS == tradeState) { + OrderStateEnum = OrderStateEnum.PAID; + } + if (Objects.nonNull(OrderStateEnum)) { + orderService.updateOrderState(Long.parseLong(parse.getOutTradeNo()), OrderStateEnum, null); + } + }).start(); + } catch (Exception e) { + throw new AppException(BizCodeEnum.ADVANCE_PAYMENT_CALLBACK_FAILED, e.toString()); + } + } + + @Override + public WxchatCallbackSuccessData queryPay(Long orderId) { + WxchatCallbackSuccessData wxchatCallbackSuccessData = new WxchatCallbackSuccessData(); + QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest(); + queryRequest.setMchid(wechatConfig.getMchId()); + queryRequest.setOutTradeNo(orderId.toString()); + // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错 + Config config = + new RSAAutoCertificateConfig.Builder() + .merchantId(wechatConfig.getMchId()) + .privateKeyFromPath(wechatConfig.getKeyPath()) + .merchantSerialNumber(wechatConfig.getMchSerialNo()) + .apiV3Key(wechatConfig.getApiV3()) + .build(); + // 构建service + JsapiService service = new JsapiService.Builder().config(config).build(); + try { + com.wechat.pay.java.service.payments.model.Transaction transaction = service.queryOrderByOutTradeNo(queryRequest); + String successTime = transaction.getSuccessTime(); + + + wxchatCallbackSuccessData.setOrderId(orderId.toString()); + wxchatCallbackSuccessData.setSuccessTime(successTime); + wxchatCallbackSuccessData.setTradetype(WeiXinConstant.getDescriptionType(transaction.getTradeType())); + wxchatCallbackSuccessData.setTradestate(WeiXinConstant.getDescriptionState(transaction.getTradeState())); + TransactionAmount amount = transaction.getAmount(); + Integer total = amount.getTotal(); + wxchatCallbackSuccessData.setTotalMoney(new BigDecimal(total).movePointLeft(2)); + } catch (ServiceException e) { + // API返回失败, 例如ORDER_NOT_EXISTS + log.error("code={}, message={}", e.getErrorCode(), e.getErrorMessage()); + log.error("reponse body={}", e.getResponseBody()); + } + return wxchatCallbackSuccessData; + } +} diff --git a/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java index 136674c..0924674 100644 --- a/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java @@ -3,6 +3,8 @@ package com.ycwl.basic.service.impl.pc; import cn.hutool.core.bean.BeanUtil; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; +import com.ycwl.basic.constant.NumberConstant; +import com.ycwl.basic.enums.OrderStateEnum; import com.ycwl.basic.mapper.pc.OrderMapper; import com.ycwl.basic.model.pc.order.entity.OrderItemEntity; import com.ycwl.basic.model.pc.order.req.OrderAddOrUpdateReq; @@ -18,6 +20,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Date; import java.util.List; /** @@ -82,4 +85,28 @@ public class OrderServiceImpl implements OrderService { } return ApiResponse.success(update); } + + /** + * 更新订单支付状态 + * + * @param orderSn 订单编号也就是订单的ID + */ + @Override + public void updateOrderState(Long orderSn, OrderStateEnum orderStateEnum, String refundReason) { + OrderAddOrUpdateReq orderAddOrUpdateReq = new OrderAddOrUpdateReq(); + orderAddOrUpdateReq.setId(orderSn); + if (orderStateEnum.getType() == NumberConstant.ONE) { + orderAddOrUpdateReq.setRefundStatus(orderStateEnum.getState()); + orderAddOrUpdateReq.setRefundAt(new Date()); + orderAddOrUpdateReq.setRefundReason(refundReason); + } else if (orderStateEnum.getType() == NumberConstant.TWO) { + int state = orderStateEnum.getState(); + orderAddOrUpdateReq.setPayAt(new Date()); + orderAddOrUpdateReq.setStatus(orderStateEnum.getState()); + if (state == OrderStateEnum.CANCELED.getState()) { + orderAddOrUpdateReq.setCancelAt(new Date()); + } + } + orderMapper.update(orderAddOrUpdateReq); + } } diff --git a/src/main/java/com/ycwl/basic/service/mobile/AppMemberService.java b/src/main/java/com/ycwl/basic/service/mobile/AppMemberService.java new file mode 100644 index 0000000..0f3b26a --- /dev/null +++ b/src/main/java/com/ycwl/basic/service/mobile/AppMemberService.java @@ -0,0 +1,52 @@ +package com.ycwl.basic.service.mobile; + +import com.ycwl.basic.model.mobile.DTO.WeChatUserInfoDTO; +import com.ycwl.basic.model.mobile.DTO.WeChatUserInfoUpdateDTO; +import com.ycwl.basic.model.pc.member.resp.MemberRespVO; +import com.ycwl.basic.utils.ApiResponse; + +import java.util.Map; + +/** + * @Author:songmingsong + */ +public interface AppMemberService { + + /** + * 获取用户的openId + * + * @return Map + */ + Map getOpenId(String code); + + /** + * 登录 + * + * @param code 前端授权码 + * @param userInfoDTO 实体信息 + * @return + */ + ApiResponse login(String code, WeChatUserInfoDTO userInfoDTO) throws Exception; + + /** + * 获取用户信息 + * + * @return + */ + ApiResponse getUserInfo(); + + /** + * 修改信息 + * + * @param userInfoUpdateDTO + * @return + */ + ApiResponse update(WeChatUserInfoUpdateDTO userInfoUpdateDTO); + + /** + * 同意用户协议 + * + * @return + */ + ApiResponse agreement(); +} diff --git a/src/main/java/com/ycwl/basic/service/mobile/WxPayService.java b/src/main/java/com/ycwl/basic/service/mobile/WxPayService.java new file mode 100644 index 0000000..703ea19 --- /dev/null +++ b/src/main/java/com/ycwl/basic/service/mobile/WxPayService.java @@ -0,0 +1,25 @@ +package com.ycwl.basic.service.mobile; + +import com.ycwl.basic.model.wxPay.WXPayOrderReqVO; +import com.ycwl.basic.model.wxPay.WxPayRespVO; +import com.ycwl.basic.model.wxPay.WxchatCallbackSuccessData; + +import javax.servlet.http.HttpServletRequest; + +public interface WxPayService { + + /** + * 微信预支付 + */ + WxPayRespVO createOrder(WXPayOrderReqVO req) throws Exception; + + /** + * 微信支付回调 + */ + void payNotify(HttpServletRequest request); + + /** + * 微信支付结果查询 + */ + WxchatCallbackSuccessData queryPay(Long orderId); +} diff --git a/src/main/java/com/ycwl/basic/service/pc/OrderService.java b/src/main/java/com/ycwl/basic/service/pc/OrderService.java index 782eecf..94cb32d 100644 --- a/src/main/java/com/ycwl/basic/service/pc/OrderService.java +++ b/src/main/java/com/ycwl/basic/service/pc/OrderService.java @@ -1,7 +1,7 @@ package com.ycwl.basic.service.pc; import com.github.pagehelper.PageInfo; -import com.ycwl.basic.model.pc.order.entity.OrderEntity; +import com.ycwl.basic.enums.OrderStateEnum; import com.ycwl.basic.model.pc.order.req.OrderAddOrUpdateReq; import com.ycwl.basic.model.pc.order.req.OrderReqQuery; import com.ycwl.basic.model.pc.order.resp.OrderRespVO; @@ -19,4 +19,7 @@ public interface OrderService { ApiResponse detail(Long orderId); ApiResponse add(OrderAddOrUpdateReq query); ApiResponse update(OrderAddOrUpdateReq query); + + + void updateOrderState(Long orderSn, OrderStateEnum orderStateEnum, String refundReason); } diff --git a/src/main/java/com/ycwl/basic/utils/ApiResponse.java b/src/main/java/com/ycwl/basic/utils/ApiResponse.java index e416d0f..bfb0fb7 100644 --- a/src/main/java/com/ycwl/basic/utils/ApiResponse.java +++ b/src/main/java/com/ycwl/basic/utils/ApiResponse.java @@ -142,8 +142,8 @@ public class ApiResponse implements Serializable { public static ApiResponse buildResult(BizCodeEnum bizCodeEnum) { ApiResponse apiResponse = new ApiResponse(); - apiResponse.setCode(bizCodeEnum.code); - apiResponse.setMsg(bizCodeEnum.message); + apiResponse.setCode(bizCodeEnum.getCode()); + apiResponse.setMsg(bizCodeEnum.getMessage()); return apiResponse; } diff --git a/src/main/java/com/ycwl/basic/utils/HttpServiceUtil.java b/src/main/java/com/ycwl/basic/utils/HttpServiceUtil.java new file mode 100644 index 0000000..cdc2f07 --- /dev/null +++ b/src/main/java/com/ycwl/basic/utils/HttpServiceUtil.java @@ -0,0 +1,31 @@ +package com.ycwl.basic.utils; + +import org.apache.http.StatusLine; + +/** + * Http服务工具类 + * @author songmingsong + */ +public class HttpServiceUtil { + /** + * 请求OK代码 + */ + public static final int REQUEST_OK_CODE=200; + /** + * 无响应内容 + */ + public static final String REQUEST_NO_RESULT="No_Result"; + + + /** + * 是否响应成功 + * @param status 响应状态 + * @return boolean + */ + public static boolean success(StatusLine status) { + return status.getStatusCode() == REQUEST_OK_CODE; + } + + + +} diff --git a/src/main/java/com/ycwl/basic/utils/JwtTokenUtil.java b/src/main/java/com/ycwl/basic/utils/JwtTokenUtil.java index 4922d71..e6c00d3 100644 --- a/src/main/java/com/ycwl/basic/utils/JwtTokenUtil.java +++ b/src/main/java/com/ycwl/basic/utils/JwtTokenUtil.java @@ -60,55 +60,4 @@ public class JwtTokenUtil { throw new CheckTokenException("token is invalid"); } } - - /*********************************************** 测试 ***************************************************/ - - /** - * 测试 token - * - * @param args - */ -// public static void main(String[] args) throws Exception { -// -// JwtInfo jwtInfo = new JwtInfo("阿豹", "1", "role1", "yangchen", 1, LocalDateTime.now(), new HashMap<>()); -// -// long a = Instant.now().toEpochMilli(); -// -// JwtTokenUtil jwtTokenUtil = new JwtTokenUtil(); -// String token = jwtTokenUtil.generateToken(jwtInfo); -// -// log.info("==> generate token: " + (Instant.now().toEpochMilli() - a) + " ms"); -// -// System.out.println("======="); -// System.out.println(); -// -// System.out.println(token); -// -// System.out.println(); -// System.out.println("======="); -// -// long b = Instant.now().toEpochMilli(); -// -// JwtInfo jwtInfo1 = jwtTokenUtil.parsingToken(token); -// -// log.info("==> paring token end " + (Instant.now().toEpochMilli() - b) + " ms"); -// -// System.out.println(); -// System.out.println(); -// System.out.println(); -// System.out.println(jwtInfo); -// System.out.println("======="); -// System.out.println(jwtInfo1.toString()); -// -// } - public static void main(String[] args) throws Exception { - JwtInfo jwtInfo = new JwtInfo(); - jwtInfo.setUserId("1"); - LocalDateTime expireTime = LocalDateTime.now().plusDays(9999999); - byte[] bytes = RsaKeyUtil.toBytes(PRI_KEY); - String token = JwtAnalysisUtil.generateToken(jwtInfo, bytes, expireTime); - System.out.println(token); - } - - } diff --git a/src/main/java/com/ycwl/basic/utils/OssUtil.java b/src/main/java/com/ycwl/basic/utils/OssUtil.java new file mode 100644 index 0000000..054d20d --- /dev/null +++ b/src/main/java/com/ycwl/basic/utils/OssUtil.java @@ -0,0 +1,85 @@ +package com.ycwl.basic.utils; + +import com.aliyun.oss.ClientException; +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.OSSException; +import com.aliyun.oss.model.PutObjectRequest; +import com.ycwl.basic.config.OssConfig; +import com.ycwl.basic.enums.BizCodeEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.InputStream; + +@Slf4j +@Component +public class OssUtil { + + @Autowired + private OssConfig ossConfig; + + public String uploadFile(InputStream inputStream, String filename) { + String uploadFileName = ossConfig.getObjectName() + System.currentTimeMillis() + filename.substring(filename.lastIndexOf(".")); + OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndPoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret()); + try { + PutObjectRequest putObjectRequest = new PutObjectRequest(ossConfig.getBucketName(), uploadFileName, inputStream); + ossClient.putObject(putObjectRequest); + String fileUrl = ossConfig.getUrl() + uploadFileName; + return fileUrl; + } catch (OSSException oe) { + log.error("Caught an OSSException, which means your request made it to OSS, " + + "but was rejected with an error response for some reason." + + " \n Error Message:" + oe.getErrorMessage() + + " \n Error Code:" + oe.getErrorCode() + + " \n Request ID:" + oe.getRequestId() + + " \n Host ID:" + oe.getHostId() + ); + + } catch (ClientException ce) { + log.error("Caught an ClientException, which means the client encountered " + + "a serious internal problem while trying to communicate with OSS, " + + "such as not being able to access the network." + + "Error Message:" + ce.getMessage()); + } finally { + if (ossClient != null) { + ossClient.shutdown(); + } + } + + return BizCodeEnum.UPLOAD_FAILED.getMessage(); + } + + + public boolean deleteFile(String filename) { + // 填写文件完整路径。文件完整路径中不能包含Bucket名称。 + String objectName = filename; + + OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndPoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret()); + try { + // 删除文件或目录。如果要删除目录,目录必须为空。 + ossClient.deleteObject(ossConfig.getBucketName(), objectName); + return true; + } catch (OSSException oe) { + log.error("Caught an OSSException, which means your request made it to OSS, " + + "but was rejected with an error response for some reason." + + " \n Error Message:" + oe.getErrorMessage() + + " \n Error Code:" + oe.getErrorCode() + + " \n Request ID:" + oe.getRequestId() + + " \n Host ID:" + oe.getHostId() + ); + } catch (ClientException ce) { + log.error("Caught an ClientException, which means the client encountered " + + "a serious internal problem while trying to communicate with OSS, " + + "such as not being able to access the network." + + "Error Message:" + ce.getMessage()); + } finally { + if (ossClient != null) { + ossClient.shutdown(); + } + } + return false; + } + +} diff --git a/src/main/java/com/ycwl/basic/utils/SslUtil.java b/src/main/java/com/ycwl/basic/utils/SslUtil.java new file mode 100644 index 0000000..a8bcd04 --- /dev/null +++ b/src/main/java/com/ycwl/basic/utils/SslUtil.java @@ -0,0 +1,98 @@ +package com.ycwl.basic.utils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * SSL工具类 + * + * @author songmingsong + */ +@Slf4j +public class SslUtil { + /** + * 获取HtttpClient对象 + * + * @return CloseableHttpClient + */ + public static CloseableHttpClient sslHttpClientBuild() { + Registry socketFactoryRegistry = + RegistryBuilder.create().register("http", PlainConnectionSocketFactory.INSTANCE) + .register("https", trustAllHttpsCertificates()).build(); + // 创建ConnectionManager,添加Connection配置信息 + PoolingHttpClientConnectionManager connectionManager = + new PoolingHttpClientConnectionManager(socketFactoryRegistry); + CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build(); + return httpClient; + } + + /** + * 信任所有Http证书 + * + * @return SSLConnectionSocketFactory + */ + private static SSLConnectionSocketFactory trustAllHttpsCertificates() { + SSLConnectionSocketFactory socketFactory = null; + TrustManager[] trustAllCerts = new TrustManager[1]; + TrustManager tm = new X509TrustManager() { + + @Override + // 返回受信任的X509证书数组。 + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + // 该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。 + // 在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。 + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + try { + if (chain!=null&&chain.length>0) { + chain[0].checkValidity(); + } + } catch (Exception e) { + log.error("checkServerTrusted",e); + } + } + + @Override + // 该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证, + // 因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。 + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + try { + if (chain!=null&&chain.length>0) { + chain[0].checkValidity(); + } + } catch (Exception e) { + log.error("checkClientTrusted",e); + } + } + }; + trustAllCerts[0] = tm; + SSLContext sc = null; + try { + sc = SSLContext.getInstance("TLSv1.2"); + sc.init(null, trustAllCerts, null); + socketFactory = new SSLConnectionSocketFactory(sc, NoopHostnameVerifier.INSTANCE); + // HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } catch (Exception e) { + log.error("trustAllHttpsCertificates", e); + } + return socketFactory; + } + +} diff --git a/src/main/java/com/ycwl/basic/utils/WXPayUtil.java b/src/main/java/com/ycwl/basic/utils/WXPayUtil.java new file mode 100644 index 0000000..081dd1f --- /dev/null +++ b/src/main/java/com/ycwl/basic/utils/WXPayUtil.java @@ -0,0 +1,49 @@ +package com.ycwl.basic.utils; + + +import com.wechat.pay.java.core.util.PemUtil; +import org.springframework.util.Base64Utils; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.util.Random; + +/** + * @Author: songmingsong + * @CreateTime: 2024-12-05 + * @Description: 微信支付 + * @Version: 1.0 + */ +public class WXPayUtil { + + private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + private static final Random RANDOM = new SecureRandom(); + + + public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException { + //replace 根据实际情况,不一定都需要 + String replace = privateKey.replace("\\n", "\n"); + PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromPath(replace); + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(merchantPrivateKey); + sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); + return Base64Utils.encodeToString(sign.sign()); + } + + /** + * 获取随机字符串 Nonce Str + * + * @return String 随机字符串 + */ + public static String generateNonceStr() { + char[] nonceChars = new char[32]; + for (int index = 0; index < nonceChars.length; ++index) { + nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length())); + } + return new String(nonceChars); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 29a10da..1366e7f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,7 +11,7 @@ spring: hiddenmethod: filter: enabled: true - datasource: # 数据源的相关配置 + datasource: # 数据源的相关配置 type: com.zaxxer.hikari.HikariDataSource # 数据源类型:HikariCP driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动 url: jdbc:mysql://8.134.112.96:3306/liuying_mgmt_re?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true @@ -60,3 +60,38 @@ mybatis-plus: # 指定使用的日志配置文件 logging: config: classpath:logback-spring.xml +# 微信小程序相关配置 +wx: + # 公众号的appId + appId: fix + # 公众号的密钥 + appSecret: fix + # 小程序的AppId + miniProgramAppId: 1 + # 小程序的secret + miniProgramSecret: 1 + # 申请openid授权 + grandType: authorization_code + # 推送模板 + push: + templateId: 1 + # 商户号 + mchId: xxxx + # 商户证书序列号 + mchSerialNo: xxxxx + # 回调接口地址 + notifyUrl: https://xxxx/a/biz/wxpay/payNotify + # 商户API私钥路径 + keyPath: module-app/src/main/resources/cert/apiclient_key.pem + # 商户APIV3密钥 + apiV3: 1 + +#阿里云OSS +aliYunOss: + endpoint: "https://oss-cn-chengdu.aliyuncs.com" + accessKeyId: "LTAI5t5cydpGpHfYqf31mEJA" + accessKeySecret: "xdFzlwzmE8QPPstbcZY82tU3xj7G0R" + bucketName: "scwzzn-file" + objectName: "image/" + url: "https://scwzzn-file.oss-cn-chengdu.aliyuncs.com/" + region: "cn-hangzhou" \ No newline at end of file