From ef9ffc61476602a557ddb8921aa96274602bcd14 Mon Sep 17 00:00:00 2001 From: Aug <17674666882@163.com> Date: Sun, 21 Sep 2025 19:04:55 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=8F=90=E7=8E=B0=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0(=E5=85=AC=E9=92=A5=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=9C=AA=E8=8E=B7=E5=8F=96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/HanHaiMemberController.java | 2 +- .../modules/hanHaiMember/entity/HanHaiMember.java | 9 +- .../hanHaiMember/mapper/HanHaiMemberMapper.java | 2 +- .../hanHaiMember/service/IHanHaiMemberService.java | 2 +- .../service/impl/HanHaiMemberServiceImpl.java | 2 +- .../modules/hanHaiMember/vue/HanHaiMemberList.vue | 12 + .../hanHaiMember/vue/modules/HanHaiMemberForm.vue | 10 + .../modules/hanHaiMember/vue3/HanHaiMember.data.ts | 23 ++ .../controller/StudytourCashoutLogController.java | 171 +++++++++ .../entity/StudytourCashoutLog.java | 84 +++++ .../mapper/StudytourCashoutLogMapper.java | 17 + .../mapper/xml/StudytourCashoutLogMapper.xml | 5 + .../service/IStudytourCashoutLogService.java | 14 + .../impl/StudytourCashoutLogServiceImpl.java | 19 + .../vue/StudytourCashoutLogList.vue | 214 ++++++++++++ .../vue/modules/StudytourCashoutLogForm.vue | 139 ++++++++ .../StudytourCashoutLogModal.Style#Drawer.vue | 84 +++++ .../vue/modules/StudytourCashoutLogModal.vue | 60 ++++ .../vue3/StudytourCashoutLog.api.ts | 61 ++++ .../vue3/StudytourCashoutLog.data.ts | 99 ++++++ .../vue3/StudytourCashoutLogList.vue | 162 +++++++++ .../vue3/components/StudytourCashoutLogModal.vue | 58 ++++ jeecg-boot-module-system/pom.xml | 7 + .../api/service/Impl/PartnerServiceImpl.java | 313 ++++++++++++++++- .../jeecg/modules/api/service/PartnerService.java | 11 + .../api/studytourController/PartnerController.java | 30 +- .../modules/api/transferUtils/TransferToUser.java | 318 +++++++++++++++++ .../modules/api/transferUtils/WXPayUtility.java | 381 +++++++++++++++++++++ .../jeecg/modules/api/wxUtils/CreateOrderReq.java | 8 + .../modules/api/wxUtils/HttpServletUtils.java | 47 +++ .../jeecg/modules/api/wxUtils/PayOrderInfo.java | 31 ++ .../jeecg/modules/api/wxUtils/QueryOrderReq.java | 23 ++ .../jeecg/modules/api/wxUtils/RefundOrderReq.java | 36 ++ .../org/jeecg/modules/api/wxUtils/Response.java | 164 +++++++++ .../api/wxUtils/WxPayAutoCertificateConfig.java | 88 +++++ .../org/jeecg/modules/api/wxUtils/WxPayConfig.java | 13 + .../src/main/resources/apiclient_key.pem | 28 ++ .../src/main/resources/application-dev.yml | 13 + .../src/main/resources/pub_key.pem | 9 + 39 files changed, 2756 insertions(+), 13 deletions(-) create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/controller/StudytourCashoutLogController.java create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/entity/StudytourCashoutLog.java create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/mapper/StudytourCashoutLogMapper.java create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/mapper/xml/StudytourCashoutLogMapper.xml create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/service/IStudytourCashoutLogService.java create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/service/impl/StudytourCashoutLogServiceImpl.java create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/StudytourCashoutLogList.vue create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogForm.vue create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogModal.Style#Drawer.vue create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogModal.vue create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLog.api.ts create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLog.data.ts create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLogList.vue create mode 100644 jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/components/StudytourCashoutLogModal.vue create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/transferUtils/TransferToUser.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/transferUtils/WXPayUtility.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/CreateOrderReq.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/HttpServletUtils.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/PayOrderInfo.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/QueryOrderReq.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/RefundOrderReq.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/Response.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/WxPayAutoCertificateConfig.java create mode 100644 jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/WxPayConfig.java create mode 100644 jeecg-boot-module-system/src/main/resources/apiclient_key.pem create mode 100644 jeecg-boot-module-system/src/main/resources/pub_key.pem diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/controller/HanHaiMemberController.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/controller/HanHaiMemberController.java index ce5cd3f..0852ca3 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/controller/HanHaiMemberController.java +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/controller/HanHaiMemberController.java @@ -39,7 +39,7 @@ import org.jeecg.common.aspect.annotation.AutoLog; /** * @Description: han_hai_member * @Author: jeecg-boot - * @Date: 2025-09-19 + * @Date: 2025-09-21 * @Version: V1.0 */ @Api(tags="han_hai_member") diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/entity/HanHaiMember.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/entity/HanHaiMember.java index 17146b2..a80495b 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/entity/HanHaiMember.java +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/entity/HanHaiMember.java @@ -20,7 +20,7 @@ import lombok.experimental.Accessors; /** * @Description: han_hai_member * @Author: jeecg-boot - * @Date: 2025-09-19 + * @Date: 2025-09-21 * @Version: V1.0 */ @Data @@ -87,7 +87,8 @@ public class HanHaiMember implements Serializable { @ApiModelProperty(value = "店铺名称备注") private java.lang.String shopName; /**角色*/ - @Excel(name = "角色", width = 15) + @Excel(name = "角色", width = 15, dicCode = "user_role") + @Dict(dicCode = "user_role") @ApiModelProperty(value = "角色") private java.lang.String role; /**ID标识号码*/ @@ -205,4 +206,8 @@ public class HanHaiMember implements Serializable { @Excel(name = "总佣金", width = 15) @ApiModelProperty(value = "总佣金") private java.math.BigDecimal commission; + /**累计提现金额*/ + @Excel(name = "累计提现金额", width = 15) + @ApiModelProperty(value = "累计提现金额") + private java.math.BigDecimal cashoutSum; } diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/mapper/HanHaiMemberMapper.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/mapper/HanHaiMemberMapper.java index 225e9db..81ba5b7 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/mapper/HanHaiMemberMapper.java +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/mapper/HanHaiMemberMapper.java @@ -9,7 +9,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @Description: han_hai_member * @Author: jeecg-boot - * @Date: 2025-09-19 + * @Date: 2025-09-21 * @Version: V1.0 */ public interface HanHaiMemberMapper extends BaseMapper { diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/service/IHanHaiMemberService.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/service/IHanHaiMemberService.java index b648fb3..d8de7f3 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/service/IHanHaiMemberService.java +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/service/IHanHaiMemberService.java @@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService; /** * @Description: han_hai_member * @Author: jeecg-boot - * @Date: 2025-09-19 + * @Date: 2025-09-21 * @Version: V1.0 */ public interface IHanHaiMemberService extends IService { diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/service/impl/HanHaiMemberServiceImpl.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/service/impl/HanHaiMemberServiceImpl.java index b114f2f..0a8b933 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/service/impl/HanHaiMemberServiceImpl.java +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/service/impl/HanHaiMemberServiceImpl.java @@ -10,7 +10,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; /** * @Description: han_hai_member * @Author: jeecg-boot - * @Date: 2025-09-19 + * @Date: 2025-09-21 * @Version: V1.0 */ @Service diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue/HanHaiMemberList.vue b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue/HanHaiMemberList.vue index 1efafa6..426e35f 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue/HanHaiMemberList.vue +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue/HanHaiMemberList.vue @@ -138,6 +138,11 @@ align:"center", dataIndex: 'phone' }, + { + title:'角色', + align:"center", + dataIndex: 'role_dictText' + }, { title:'邀请人', align:"center", @@ -161,6 +166,11 @@ align:"center", dataIndex: 'commission' }, + { + title:'累计提现金额', + align:"center", + dataIndex: 'cashoutSum' + }, { title: '操作', dataIndex: 'action', @@ -198,10 +208,12 @@ fieldList.push({type:'string',value:'nickName',text:'昵称',dictCode:''}) fieldList.push({type:'string',value:'headImage',text:'用户头像',dictCode:''}) fieldList.push({type:'string',value:'phone',text:'手机号码',dictCode:''}) + fieldList.push({type:'string',value:'role',text:'角色',dictCode:'user_role'}) fieldList.push({type:'string',value:'vid',text:'邀请人',dictCode:"han_hai_member,nick_name,id"}) fieldList.push({type:'date',value:'vtime',text:'邀请时间'}) fieldList.push({type:'BigDecimal',value:'price',text:'余额',dictCode:''}) fieldList.push({type:'BigDecimal',value:'commission',text:'总佣金',dictCode:''}) + fieldList.push({type:'BigDecimal',value:'cashoutSum',text:'累计提现金额',dictCode:''}) this.superFieldList = fieldList } } diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue/modules/HanHaiMemberForm.vue b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue/modules/HanHaiMemberForm.vue index 678bc71..1364f6a 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue/modules/HanHaiMemberForm.vue +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue/modules/HanHaiMemberForm.vue @@ -18,6 +18,11 @@ + + + + + @@ -38,6 +43,11 @@ + + + + + diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue3/HanHaiMember.data.ts b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue3/HanHaiMember.data.ts index 7a77dbd..56438ec 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue3/HanHaiMember.data.ts +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/hanHaiMember/vue3/HanHaiMember.data.ts @@ -20,6 +20,11 @@ export const columns: BasicColumn[] = [ align:"center", dataIndex: 'phone' }, + { + title: '角色', + align:"center", + dataIndex: 'role_dictText' + }, { title: '邀请人', align:"center", @@ -43,6 +48,11 @@ export const columns: BasicColumn[] = [ align:"center", dataIndex: 'commission' }, + { + title: '累计提现金额', + align:"center", + dataIndex: 'cashoutSum' + }, ]; //查询数据 export const searchFormSchema: FormSchema[] = [ @@ -72,6 +82,14 @@ export const formSchema: FormSchema[] = [ ]; }, }, + { + label: '角色', + field: 'role', + component: 'JDictSelectTag', + componentProps:{ + dictCode:"user_role" + }, + }, { label: '邀请人', field: 'vid', @@ -95,4 +113,9 @@ export const formSchema: FormSchema[] = [ field: 'commission', component: 'InputNumber', }, + { + label: '累计提现金额', + field: 'cashoutSum', + component: 'InputNumber', + }, ]; diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/controller/StudytourCashoutLogController.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/controller/StudytourCashoutLogController.java new file mode 100644 index 0000000..a2db277 --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/controller/StudytourCashoutLogController.java @@ -0,0 +1,171 @@ +package org.jeecg.modules.studytourCashoutLog.controller; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.modules.studytourCashoutLog.entity.StudytourCashoutLog; +import org.jeecg.modules.studytourCashoutLog.service.IStudytourCashoutLogService; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; + +import org.jeecgframework.poi.excel.ExcelImportUtil; +import org.jeecgframework.poi.excel.def.NormalExcelConstants; +import org.jeecgframework.poi.excel.entity.ExportParams; +import org.jeecgframework.poi.excel.entity.ImportParams; +import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; +import org.jeecg.common.system.base.controller.JeecgController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.servlet.ModelAndView; +import com.alibaba.fastjson.JSON; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.jeecg.common.aspect.annotation.AutoLog; + + /** + * @Description: 提现记录表 + * @Author: jeecg-boot + * @Date: 2025-09-21 + * @Version: V1.0 + */ +@Api(tags="提现记录表") +@RestController +@RequestMapping("/studytourCashoutLog/studytourCashoutLog") +@Slf4j +public class StudytourCashoutLogController extends JeecgController { + @Autowired + private IStudytourCashoutLogService studytourCashoutLogService; + + /** + * 分页列表查询 + * + * @param studytourCashoutLog + * @param pageNo + * @param pageSize + * @param req + * @return + */ + //@AutoLog(value = "提现记录表-分页列表查询") + @ApiOperation(value="提现记录表-分页列表查询", notes="提现记录表-分页列表查询") + @GetMapping(value = "/list") + public Result> queryPageList(StudytourCashoutLog studytourCashoutLog, + @RequestParam(name="pageNo", defaultValue="1") Integer pageNo, + @RequestParam(name="pageSize", defaultValue="10") Integer pageSize, + HttpServletRequest req) { + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(studytourCashoutLog, req.getParameterMap()); + Page page = new Page(pageNo, pageSize); + IPage pageList = studytourCashoutLogService.page(page, queryWrapper); + return Result.OK(pageList); + } + + /** + * 添加 + * + * @param studytourCashoutLog + * @return + */ + @AutoLog(value = "提现记录表-添加") + @ApiOperation(value="提现记录表-添加", notes="提现记录表-添加") + @PostMapping(value = "/add") + public Result add(@RequestBody StudytourCashoutLog studytourCashoutLog) { + studytourCashoutLogService.save(studytourCashoutLog); + return Result.OK("添加成功!"); + } + + /** + * 编辑 + * + * @param studytourCashoutLog + * @return + */ + @AutoLog(value = "提现记录表-编辑") + @ApiOperation(value="提现记录表-编辑", notes="提现记录表-编辑") + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST}) + public Result edit(@RequestBody StudytourCashoutLog studytourCashoutLog) { + studytourCashoutLogService.updateById(studytourCashoutLog); + return Result.OK("编辑成功!"); + } + + /** + * 通过id删除 + * + * @param id + * @return + */ + @AutoLog(value = "提现记录表-通过id删除") + @ApiOperation(value="提现记录表-通过id删除", notes="提现记录表-通过id删除") + @DeleteMapping(value = "/delete") + public Result delete(@RequestParam(name="id",required=true) String id) { + studytourCashoutLogService.removeById(id); + return Result.OK("删除成功!"); + } + + /** + * 批量删除 + * + * @param ids + * @return + */ + @AutoLog(value = "提现记录表-批量删除") + @ApiOperation(value="提现记录表-批量删除", notes="提现记录表-批量删除") + @DeleteMapping(value = "/deleteBatch") + public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) { + this.studytourCashoutLogService.removeByIds(Arrays.asList(ids.split(","))); + return Result.OK("批量删除成功!"); + } + + /** + * 通过id查询 + * + * @param id + * @return + */ + //@AutoLog(value = "提现记录表-通过id查询") + @ApiOperation(value="提现记录表-通过id查询", notes="提现记录表-通过id查询") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name="id",required=true) String id) { + StudytourCashoutLog studytourCashoutLog = studytourCashoutLogService.getById(id); + if(studytourCashoutLog==null) { + return Result.error("未找到对应数据"); + } + return Result.OK(studytourCashoutLog); + } + + /** + * 导出excel + * + * @param request + * @param studytourCashoutLog + */ + @RequestMapping(value = "/exportXls") + public ModelAndView exportXls(HttpServletRequest request, StudytourCashoutLog studytourCashoutLog) { + return super.exportXls(request, studytourCashoutLog, StudytourCashoutLog.class, "提现记录表"); + } + + /** + * 通过excel导入数据 + * + * @param request + * @param response + * @return + */ + @RequestMapping(value = "/importExcel", method = RequestMethod.POST) + public Result importExcel(HttpServletRequest request, HttpServletResponse response) { + return super.importExcel(request, response, StudytourCashoutLog.class); + } + +} diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/entity/StudytourCashoutLog.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/entity/StudytourCashoutLog.java new file mode 100644 index 0000000..522489e --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/entity/StudytourCashoutLog.java @@ -0,0 +1,84 @@ +package org.jeecg.modules.studytourCashoutLog.entity; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.math.BigDecimal; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.springframework.format.annotation.DateTimeFormat; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.jeecg.common.aspect.annotation.Dict; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * @Description: 提现记录表 + * @Author: jeecg-boot + * @Date: 2025-09-21 + * @Version: V1.0 + */ +@Data +@TableName("studytour_cashout_log") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@ApiModel(value="studytour_cashout_log对象", description="提现记录表") +public class StudytourCashoutLog implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + @ApiModelProperty(value = "主键") + private java.lang.String id; + /**创建人*/ + @ApiModelProperty(value = "创建人") + private java.lang.String createBy; + /**创建日期*/ + @ApiModelProperty(value = "创建日期") + private java.util.Date createTime; + /**更新人*/ + @ApiModelProperty(value = "更新人") + private java.lang.String updateBy; + /**更新日期*/ + @ApiModelProperty(value = "更新日期") + private java.util.Date updateTime; + /**提现者姓名*/ + @Excel(name = "提现者姓名", width = 15) + @ApiModelProperty(value = "提现者姓名") + private java.lang.String realName; + /**提现金额*/ + @Excel(name = "提现金额", width = 15) + @ApiModelProperty(value = "提现金额") + private java.math.BigDecimal amount; + /**提现状态*/ + @Excel(name = "提现状态", width = 15, dicCode = "cashout_status") + @Dict(dicCode = "cashout_status") + @ApiModelProperty(value = "提现状态") + private java.lang.String status; + /**到账时间*/ + @Excel(name = "到账时间", width = 15) + @ApiModelProperty(value = "到账时间") + private java.util.Date paymentTime; + /**商户单号*/ + @Excel(name = "商户单号", width = 15) + @ApiModelProperty(value = "商户单号") + private java.lang.String outBillNo; + /**微信转账单号*/ + @Excel(name = "微信转账单号", width = 15) + @ApiModelProperty(value = "微信转账单号") + private java.lang.String transferBillNo; + /**领取转账信息参数*/ + @Excel(name = "领取转账信息参数", width = 15) + @ApiModelProperty(value = "领取转账信息参数") + private java.lang.String packageInfo; + /**关联用户id*/ + @Excel(name = "关联用户id", width = 15, dictTable = "han_hai_member", dicText = "nick_name", dicCode = "id") + @Dict(dictTable = "han_hai_member", dicText = "nick_name", dicCode = "id") + @ApiModelProperty(value = "关联用户id") + private java.lang.String userId; +} diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/mapper/StudytourCashoutLogMapper.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/mapper/StudytourCashoutLogMapper.java new file mode 100644 index 0000000..6bf785f --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/mapper/StudytourCashoutLogMapper.java @@ -0,0 +1,17 @@ +package org.jeecg.modules.studytourCashoutLog.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Param; +import org.jeecg.modules.studytourCashoutLog.entity.StudytourCashoutLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * @Description: 提现记录表 + * @Author: jeecg-boot + * @Date: 2025-09-21 + * @Version: V1.0 + */ +public interface StudytourCashoutLogMapper extends BaseMapper { + +} diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/mapper/xml/StudytourCashoutLogMapper.xml b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/mapper/xml/StudytourCashoutLogMapper.xml new file mode 100644 index 0000000..710d7a5 --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/mapper/xml/StudytourCashoutLogMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/service/IStudytourCashoutLogService.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/service/IStudytourCashoutLogService.java new file mode 100644 index 0000000..bb3f9ef --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/service/IStudytourCashoutLogService.java @@ -0,0 +1,14 @@ +package org.jeecg.modules.studytourCashoutLog.service; + +import org.jeecg.modules.studytourCashoutLog.entity.StudytourCashoutLog; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * @Description: 提现记录表 + * @Author: jeecg-boot + * @Date: 2025-09-21 + * @Version: V1.0 + */ +public interface IStudytourCashoutLogService extends IService { + +} diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/service/impl/StudytourCashoutLogServiceImpl.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/service/impl/StudytourCashoutLogServiceImpl.java new file mode 100644 index 0000000..a2ac387 --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/service/impl/StudytourCashoutLogServiceImpl.java @@ -0,0 +1,19 @@ +package org.jeecg.modules.studytourCashoutLog.service.impl; + +import org.jeecg.modules.studytourCashoutLog.entity.StudytourCashoutLog; +import org.jeecg.modules.studytourCashoutLog.mapper.StudytourCashoutLogMapper; +import org.jeecg.modules.studytourCashoutLog.service.IStudytourCashoutLogService; +import org.springframework.stereotype.Service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +/** + * @Description: 提现记录表 + * @Author: jeecg-boot + * @Date: 2025-09-21 + * @Version: V1.0 + */ +@Service +public class StudytourCashoutLogServiceImpl extends ServiceImpl implements IStudytourCashoutLogService { + +} diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/StudytourCashoutLogList.vue b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/StudytourCashoutLogList.vue new file mode 100644 index 0000000..619e635 --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/StudytourCashoutLogList.vue @@ -0,0 +1,214 @@ + + + + \ No newline at end of file diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogForm.vue b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogForm.vue new file mode 100644 index 0000000..e4f758c --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogForm.vue @@ -0,0 +1,139 @@ + + + \ No newline at end of file diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogModal.Style#Drawer.vue b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogModal.Style#Drawer.vue new file mode 100644 index 0000000..d5a670b --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogModal.Style#Drawer.vue @@ -0,0 +1,84 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogModal.vue b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogModal.vue new file mode 100644 index 0000000..d24e22c --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue/modules/StudytourCashoutLogModal.vue @@ -0,0 +1,60 @@ + + + \ No newline at end of file diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLog.api.ts b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLog.api.ts new file mode 100644 index 0000000..58ca26a --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLog.api.ts @@ -0,0 +1,61 @@ +import {defHttp} from '/@/utils/http/axios'; +import {Modal} from 'ant-design-vue'; + +enum Api { + list = '/studytourCashoutLog/studytourCashoutLog/list', + save='/studytourCashoutLog/studytourCashoutLog/add', + edit='/studytourCashoutLog/studytourCashoutLog/edit', + deleteOne = '/studytourCashoutLog/studytourCashoutLog/delete', + deleteBatch = '/studytourCashoutLog/studytourCashoutLog/deleteBatch', + importExcel = '/studytourCashoutLog/studytourCashoutLog/importExcel', + exportXls = '/studytourCashoutLog/studytourCashoutLog/exportXls', +} +/** + * 导出api + * @param params + */ +export const getExportUrl = Api.exportXls; +/** + * 导入api + */ +export const getImportUrl = Api.importExcel; +/** + * 列表接口 + * @param params + */ +export const list = (params) => + defHttp.get({url: Api.list, params}); + +/** + * 删除单个 + */ +export const deleteOne = (params,handleSuccess) => { + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); +} +/** + * 批量删除 + * @param params + */ +export const batchDelete = (params, handleSuccess) => { + Modal.confirm({ + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); + } + }); +} +/** + * 保存或者更新 + * @param params + */ +export const saveOrUpdate = (params, isUpdate) => { + let url = isUpdate ? Api.edit : Api.save; + return defHttp.post({url: url, params}); +} diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLog.data.ts b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLog.data.ts new file mode 100644 index 0000000..2edd30a --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLog.data.ts @@ -0,0 +1,99 @@ +import {BasicColumn} from '/@/components/Table'; +import {FormSchema} from '/@/components/Table'; +import { rules} from '/@/utils/helper/validator'; +import { render } from '/@/utils/common/renderUtils'; +//列表数据 +export const columns: BasicColumn[] = [ + { + title: '提现者姓名', + align:"center", + dataIndex: 'realName' + }, + { + title: '提现金额', + align:"center", + dataIndex: 'amount' + }, + { + title: '提现状态', + align:"center", + dataIndex: 'status_dictText' + }, + { + title: '到账时间', + align:"center", + dataIndex: 'paymentTime' + }, + { + title: '商户单号', + align:"center", + dataIndex: 'outBillNo' + }, + { + title: '微信转账单号', + align:"center", + dataIndex: 'transferBillNo' + }, + { + title: '领取转账信息参数', + align:"center", + dataIndex: 'packageInfo' + }, + { + title: '关联用户id', + align:"center", + dataIndex: 'userId_dictText' + }, +]; +//查询数据 +export const searchFormSchema: FormSchema[] = [ +]; +//表单数据 +export const formSchema: FormSchema[] = [ + { + label: '提现者姓名', + field: 'realName', + component: 'Input', + }, + { + label: '提现金额', + field: 'amount', + component: 'InputNumber', + }, + { + label: '提现状态', + field: 'status', + component: 'JDictSelectTag', + componentProps:{ + dictCode:"cashout_status" + }, + }, + { + label: '到账时间', + field: 'paymentTime', + component: 'Input', + }, + { + label: '商户单号', + field: 'outBillNo', + component: 'Input', + }, + { + label: '微信转账单号', + field: 'transferBillNo', + component: 'Input', + }, + { + label: '领取转账信息参数', + field: 'packageInfo', + component: 'Input', + }, + { + label: '关联用户id', + field: 'userId', + component: 'JDictSelectTag', + componentProps:{ + dictCode:"han_hai_member,nick_name,id" + }, + }, +]; diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLogList.vue b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLogList.vue new file mode 100644 index 0000000..82bbb1a --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/StudytourCashoutLogList.vue @@ -0,0 +1,162 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/components/StudytourCashoutLogModal.vue b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/components/StudytourCashoutLogModal.vue new file mode 100644 index 0000000..300836c --- /dev/null +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/studytourCashoutLog/vue3/components/StudytourCashoutLogModal.vue @@ -0,0 +1,58 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot-module-system/pom.xml b/jeecg-boot-module-system/pom.xml index 723dda4..9e73921 100644 --- a/jeecg-boot-module-system/pom.xml +++ b/jeecg-boot-module-system/pom.xml @@ -74,6 +74,13 @@ compile + + + com.github.wechatpay-apiv3 + wechatpay-java + 0.2.15 + + diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/Impl/PartnerServiceImpl.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/Impl/PartnerServiceImpl.java index 4764c47..8359f65 100644 --- a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/Impl/PartnerServiceImpl.java +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/Impl/PartnerServiceImpl.java @@ -4,19 +4,26 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.map.HashedMap; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.lang.StringUtils; import org.jeecg.common.api.vo.Result; import org.jeecg.config.shiro.ShiroRealm; import org.jeecg.modules.api.bean.HttpClientUtil; import org.jeecg.modules.api.bean.PageBean; import org.jeecg.modules.api.bean.WxQrCodeVo; import org.jeecg.modules.api.service.PartnerService; +import org.jeecg.modules.api.transferUtils.TransferToUser; +import org.jeecg.modules.api.utils.CommonUtils; import org.jeecg.modules.hanHaiMember.entity.HanHaiMember; import org.jeecg.modules.hanHaiMember.service.IHanHaiMemberService; +import org.jeecg.modules.studytourCashoutLog.entity.StudytourCashoutLog; +import org.jeecg.modules.studytourCashoutLog.service.IStudytourCashoutLogService; import org.jeecg.modules.studytourCommissionLog.entity.StudytourCommissionLog; import org.jeecg.modules.studytourCommissionLog.service.IStudytourCommissionLogService; import org.jeecg.modules.studytourConfig.entity.StudytourConfig; @@ -33,6 +40,7 @@ import org.springframework.web.multipart.commons.CommonsMultipartFile; import javax.annotation.Resource; import java.io.*; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -43,8 +51,33 @@ import java.util.Map; public class PartnerServiceImpl implements PartnerService { /******************************************************************************************************************/ - private String appid = "wxee64675d48680dd4";//小程序appid - private String secret = "d7e3f49858350cf40b1aeeb8fa1c61eb";//小程序密钥 + //微信公众号的 AppID + @Value("${wx.miniapp.appid}") + private String appid; + //微信公众号的密钥 + @Value("${wx.miniapp.secret}") + private String secret; + //商户号 + @Value("${wx.miniapp.merchantId}") + private String mchid; + //商户APIV3密钥 + @Value("${wx.miniapp.apiV3Key}") + private String apiV3Key; + //商户API私钥路径 + @Value("${wx.miniapp.privateKeyPath}") + private String privateKeyFilePath; + //商户API公钥路径 + @Value("${wx.miniapp.publicKeyPath}") + private String wechatPayPublicKeyFilePath; + //商户API公钥ID + @Value("${wx.miniapp.publicKeyId}") + private String wechatPayPublicKeyId; + //商户证书序列号 + @Value("${wx.miniapp.merchantSerialNumber}") + private String certiticateSerialNo; + +// private String appid = "wxee64675d48680dd4";//小程序appid +// private String secret = "d7e3f49858350cf40b1aeeb8fa1c61eb";//小程序密钥 @Value("${jeecg.oss.endpoint}") private String endpoint; @Value("${jeecg.oss.accessKey}") @@ -65,6 +98,9 @@ public class PartnerServiceImpl implements PartnerService { //佣金记录 @Resource private IStudytourCommissionLogService studytourCommissionLogService; + //提现记录 + @Resource + private IStudytourCashoutLogService studytourCashoutLogService; //配置信息 @Resource private IStudytourConfigService studytourConfigService; @@ -365,4 +401,277 @@ public class PartnerServiceImpl implements PartnerService { ossClient.shutdown(); return dbpath; } + + + @Override + public Result queryCashoutLog(String token, PageBean pageBean) { + log.info("获取提现记录列表开始"); + //权限验证 + HanHaiMember hanHaiMember = shiroRealm.checkUserTokenIsEffectHanHaiOpenId(token);//小程序权限验证 + //HanHaiMember hanHaiMember = hanHaiMemberService.getById(token); + //返回信息 + String massege = ""; + //分页信息 + Page page = null; + //查询信息 + LambdaQueryChainWrapper query = null; + //返回信息 + Page pageList = null; + + try{ + //分页 + page = new Page(pageBean.getPageNo(), pageBean.getPageSize()); + query = studytourCashoutLogService + .lambdaQuery(); + + //组装查询条件 + query.eq(StudytourCashoutLog::getUserId, hanHaiMember.getId()); + + //按照创建时间降序排列 + query.orderByDesc(StudytourCashoutLog::getCreateTime); + + //获取轮播图信息 + pageList = query.page(page); + + log.info("获取提现记录列表结束"); + return Result.OK("提现记录列表", pageList); + }catch (Exception e){ + e.printStackTrace(); + log.info("获取提现记录列表失败{}", e.getMessage()); + return Result.error("提现记录列表查询失败"); + } + } + + //提现 + @Override + public Result cashOut(String token, String userName, BigDecimal transferAmount) { + //权限验证 + HanHaiMember hanHaiMember = shiroRealm.checkUserTokenIsEffectHanHaiOpenId(token);//小程序权限验证 + + //提现结果 + String massage = "提现申请失败"; + + try{ + //0、基础约束 + BigDecimal balance = hanHaiMember.getPrice();//用户余额 + //金额不能为空 + if(null == transferAmount){ + log.info("金额为空,请填写大于0的整数金额"); + return Result.error("金额小于0,请填写大于0的整数金额"); + } + if(transferAmount.compareTo(balance)>0){ + //提现金额大于用户余额 + log.info("用户余额不足,当前用户余额:{}", balance); + return Result.error("用户余额不足"); + } + //提现金额要为整数 + if(transferAmount.scale()>0){ + log.info("请填写大于0的整数金额,当前输入金额:{}", transferAmount); + return Result.error("请填写大于0的整数金额"); + } + //提现金额大于0的整数 + if(transferAmount.compareTo(new BigDecimal(0))<=0){ + log.info("请填写大于0的整数金额,当前输入金额:{}", transferAmount); + return Result.error("请填写大于0的整数金额"); + } + if(StringUtils.isEmpty(userName)){ + log.info("用户姓名未填写"); + return Result.error("用户姓名未填写"); + } + + + //1.微信提现基础参数 + Map map = getMap(); + //变化的用户信息参数 + map.put("openid", hanHaiMember.getAppletOpenid());//用户openid(小程序) + map.put("userName", userName);//用户真实姓名 + map.put("transferAmount", transferAmount);//提现金额, 单位为“分” + String idStr = "H" + IdWorker.getIdStr(); + map.put("outBillNo", idStr);//商户单号 + + TransferToUser client = new TransferToUser( + map.get("mchid").toString(), // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 + map.get("certiticateSerialNo").toString(), // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 + map.get("privateKeyFilePath").toString(), // 商户API证书私钥文件路径,本地文件路径 + map.get("wechatPayPublicKeyId").toString(), // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 + map.get("wechatPayPublicKeyFilePath").toString() // 微信支付公钥文件路径,本地文件路径 + ); + + //2、场景信息 + TransferToUser.TransferToUserRequest request = new TransferToUser.TransferToUserRequest(); + request.appid = map.get("appid").toString(); + request.outBillNo = map.get("outBillNo").toString(); + request.transferSceneId = map.get("transferSceneId").toString(); + request.openid = map.get("openid").toString(); + request.userName = client.encrypt(map.get("userName").toString()); + request.transferAmount = transferAmount.longValue()*100;//单位为分 + request.transferRemark = map.get("transferRemark").toString(); + request.notifyUrl = map.get("notifyUrl").toString(); + request.userRecvPerception = map.get("userRecvPerception").toString(); + request.transferSceneReportInfos = new ArrayList<>(); + { + TransferToUser.TransferSceneReportInfo item0 = new TransferToUser.TransferSceneReportInfo(); + item0.infoType = map.get("infoType1").toString(); + item0.infoContent = map.get("infoContent1").toString(); + request.transferSceneReportInfos.add(item0); + TransferToUser.TransferSceneReportInfo item1 = new TransferToUser.TransferSceneReportInfo(); + item1.infoType = map.get("infoType2").toString(); + item1.infoContent = map.get("infoContent2").toString(); + request.transferSceneReportInfos.add(item1); + } + + //3、执行提现 + TransferToUser.TransferToUserResponse response = client.run(request, map); + log.info("提现发起成功,outBillNo:"+response.outBillNo + ",transferBillNo:" +response.transferBillNo + ",state:" +response.state+ ",packageInfo:" + response.packageInfo); + switch (response.state){ + case ACCEPTED: + log.info("转账已受理"); + massage = "转账已受理"; + break; + case PROCESSING: + log.info("转账锁定资金中。如果一直停留在该状态,建议检查账户余额是否足够,如余额不足,可充值后再原单重试"); + massage = "转账锁定资金中。如果一直停留在该状态,建议检查账户余额是否足够,如余额不足,可充值后再原单重试"; + break; + case WAIT_USER_CONFIRM: + log.info("待收款用户确认,可拉起微信收款确认页面进行收款确认"); + massage = "待收款用户确认,可拉起微信收款确认页面进行收款确认"; + break; + case TRANSFERING: + log.info("转账中,可拉起微信收款确认页面再次重试确认收款"); + massage = "转账中,可拉起微信收款确认页面再次重试确认收款"; + break; + case SUCCESS: + log.info("转账成功"); + massage = "转账成功"; + break; + case FAIL: + log.info("转账失败"); + massage = "转账失败"; + break; + case CANCELING: + log.info("商户撤销请求受理成功,该笔转账正在撤销中"); + massage = "商户撤销请求受理成功,该笔转账正在撤销中"; + break; + case CANCELLED: + log.info("转账撤销完成"); + massage = "转账撤销完成"; + break; + } + + log.info("提现结果:" + massage); + + //4、业务处理 + StudytourCashoutLog cashoutLog = new StudytourCashoutLog(); + cashoutLog.setAmount(transferAmount); + cashoutLog.setRealName(userName); + cashoutLog.setId(response.outBillNo); + cashoutLog.setOutBillNo(response.outBillNo); + cashoutLog.setTransferBillNo(response.transferBillNo); + cashoutLog.setPackageInfo(response.packageInfo); + cashoutLog.setUserId(hanHaiMember.getId()); + studytourCashoutLogService.save(cashoutLog); + + //5、返回信息 + return Result.OK(massage, response); + }catch (Exception e){ + e.printStackTrace(); + log.info("提现失败:" + e.getMessage()); + return Result.error(e.getMessage()); + + } + } + + //领取提现金额 + @Override + public Result getMoney(String token, String id) { + log.info("领取提现金额"); + HanHaiMember hanHaiMember = shiroRealm.checkUserTokenIsEffectHanHaiOpenId(token);//小程序权限验证 + + try{ + StudytourCashoutLog cashoutLog = studytourCashoutLogService + .lambdaQuery() + .eq(StudytourCashoutLog::getUserId, hanHaiMember.getId()) + .eq(StudytourCashoutLog::getId, id) + .eq(StudytourCashoutLog::getStatus, "0") + .one(); + if(null != cashoutLog){ + //提现金额增加 + BigDecimal oldCashoutSum = hanHaiMember.getCashoutSum(); + BigDecimal newCashoutSum = oldCashoutSum.add(cashoutLog.getAmount()); + hanHaiMember.setCashoutSum(newCashoutSum); + hanHaiMemberService.updateById(hanHaiMember); + + //提现日志记录 + cashoutLog.setStatus("1"); + cashoutLog.setPaymentTime(CommonUtils.getCurrentTime()); + studytourCashoutLogService.updateById(cashoutLog); + + //用户余额减少 + BigDecimal oldBalance = hanHaiMember.getPrice(); + BigDecimal newBalance = oldBalance.subtract(cashoutLog.getAmount()); + hanHaiMember.setPrice(newBalance); + hanHaiMemberService.updateById(hanHaiMember); + + log.info("领取提现金额结束"); + return Result.OK("领取提现结束"); + }else { + log.error("领取提现金额失败"); + return Result.error("提现内容不存在,请检查提现记录id:" + id); + } + + }catch (Exception e){ + e.printStackTrace(); + log.error("领取提现金额失败"); + return Result.OK("领取提现失败"); + } + } + + /** + * 微信提现基础参数 + * @return + */ + public Map getMap(){ + Map map = new HashedMap();//转账接口所需参数 + map.put("host", "https://api.mch.weixin.qq.com");//请求地址 + map.put("method", "POST");//请求类型 + map.put("path", "/v3/fund-app/mch-transfer/transfer-bills");//提现接口 + map.put("notifyUrl", "https://www.yurangongfang.com/massage-admin/massage/cash/cashoutNotify/");//回调接口 + + //微信商户参数 +// map.put("appid", "wx77ba4c7131677a74");//小程序appid +// map.put("mchid", "1712378227");//商户号 +// map.put("certiticateSerialNo", "33E9FE8076531A7C7AD401DC34E053DBD7C28E22");//商户序列号 +// map.put("privateKeyFilePath", "jeecg-boot-module-system/src/main/resources/apiclient_key.pem");//商户私钥证书 +// map.put("wechatPayPublicKeyId", "PUB_KEY_ID_0117123782272025033100396400002931");//商户公钥id +// map.put("wechatPayPublicKeyFilePath", "jeecg-boot-module-system/src/main/resources/pub_key.pem");//商户公钥证书 + map.put("appid", appid);//小程序appid + map.put("mchid", mchid);//商户号 + map.put("certiticateSerialNo", certiticateSerialNo);//商户序列号 + map.put("privateKeyFilePath", privateKeyFilePath);//商户私钥证书 + map.put("wechatPayPublicKeyId", wechatPayPublicKeyId);//商户公钥id + map.put("wechatPayPublicKeyFilePath", wechatPayPublicKeyFilePath);//商户公钥证书 + map.put("transferSceneId", "1005");//商户转账场景ID 1005-佣金报酬 + map.put("transferRemark", "佣金报酬");//商户转账场景ID 1005-佣金报酬 + map.put("userRecvPerception", "劳务报酬");//商户转账场景ID 1005-佣金报酬 + + + //变化的用户信息参数 +// map.put("openid", "oFzrW4migndUepy7zYgYO2YoZ5to");//用户openid +// map.put("userName", "唐斌");//用户真实姓名 +// map.put("transferAmount", 100L);//提现金额, 单位为“分” +// String idStr = "H" + IdWorker.getIdStr(); +// map.put("outBillNo", idStr);//商户单号 + + + //转账场景报备信息,ransfer_scene_report_infos为数组类型参数,在现金营销的转账场景下需固定传两条明细,每条“转账场景报备信息明细”包含info_type、info_content两个参数。 + map.put("infoType1","岗位类型"); + map.put("infoContent1","外卖员"); + map.put("infoType2","报酬说明"); + map.put("infoContent2","高温补贴"); + + return map; + + } + + } diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/PartnerService.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/PartnerService.java index f31b5fd..c397392 100644 --- a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/PartnerService.java +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/PartnerService.java @@ -7,6 +7,8 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import java.math.BigDecimal; + public interface PartnerService { //我的团队-获取直推用户列表 @@ -21,4 +23,13 @@ public interface PartnerService { //合伙人-邀请二维码 public Result getInviteCode(String path); + //提现记录列表 + public Result queryCashoutLog(String token, PageBean pageBean); + + //提现 + public Result cashOut(String token, String userName, BigDecimal transferAmount); + + //领取提现金额 + public Result getMoney(String token, String id); + } diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/studytourController/PartnerController.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/studytourController/PartnerController.java index caa2dfc..0e6757f 100644 --- a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/studytourController/PartnerController.java +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/studytourController/PartnerController.java @@ -6,10 +6,7 @@ import lombok.extern.slf4j.Slf4j; import org.jeecg.common.api.vo.Result; import org.jeecg.modules.api.bean.PageBean; import org.jeecg.modules.api.service.PartnerService; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.math.BigDecimal; @@ -56,10 +53,33 @@ public class PartnerController { return partnerService.getInviteCode(path); } + + @ApiOperation(value="钱包-提现记录列表", notes="钱包-提现记录列表") + @RequestMapping(value = "/queryCashoutLog", method = {RequestMethod.GET}) + public Result queryCashoutLog(@RequestHeader("X-Access-Token") String token, PageBean pageBean){ + return partnerService.queryCashoutLog(token, pageBean); + } + @ApiOperation(value="合伙人-提现", notes="合伙人-提现") @RequestMapping(value = "/cashout", method = {RequestMethod.POST}) public Result cashout(@RequestHeader("X-Access-Token") String token, String userName, BigDecimal transferAmount){ - return Result.OK("接口暂未处理"); + return partnerService.cashOut(token, userName, transferAmount); } + @ApiOperation(value="钱包-领取提现金额", notes="钱包-领取提现金额") + @RequestMapping(value = "/getMoney", method = {RequestMethod.POST}) + public Result getMoney(@RequestHeader("X-Access-Token") String token, String id){ + return partnerService.getMoney(token, id); + } + +// //提现回调 +// @PostMapping("/cashoutNotify") +// public Object cashoutNotify(@RequestHeader("Wechatpay-Signature") String signature, +// @RequestHeader("Wechatpay-Timestamp") String timestamp, +// @RequestHeader("Wechatpay-Nonce") String nonce, +// @RequestHeader("Wechatpay-Serial") String serial, +// @RequestBody String requestBody){ +// return partnerService.cashoutNotify(signature, timestamp, nonce, serial, requestBody); +// } + } diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/transferUtils/TransferToUser.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/transferUtils/TransferToUser.java new file mode 100644 index 0000000..a42ee13 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/transferUtils/TransferToUser.java @@ -0,0 +1,318 @@ +package org.jeecg.modules.api.transferUtils; + +import com.google.gson.annotations.SerializedName; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.List; +import java.util.Map; + +/** + * 发起转账 + */ +@Slf4j +public class TransferToUser { + + private final String mchid; + private final String certificateSerialNo; + private final PrivateKey privateKey; + private final String wechatPayPublicKeyId; + private final PublicKey wechatPayPublicKey; + + public TransferToUser(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) { + this.mchid = mchid; + this.certificateSerialNo = certificateSerialNo; + this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath); + this.wechatPayPublicKeyId = wechatPayPublicKeyId; + this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath); + } + +// /** +// * 微信提现基础参数 +// * @return +// */ +// public static Map getMap(){ +// Map map = new HashedMap();//转账接口所需参数 +// map.put("host", "https://api.mch.weixin.qq.com");//请求地址 +// map.put("method", "POST");//请求类型 +// map.put("path", "/v3/fund-app/mch-transfer/transfer-bills");//提现接口 +// map.put("notifyUrl", "https://www.yurangongfang.com/massage-admin/massage/cash/cashoutNotify/");//回调接口 +// +//// //微信商户参数 +//// map.put("appid", "wx77ba4c7131677a74");//小程序appid +//// map.put("mchid", "1712378227");//商户号 +//// map.put("certiticateSerialNo", "33E9FE8076531A7C7AD401DC34E053DBD7C28E22");//商户序列号 +//// map.put("privateKeyFilePath", "jeecg-boot-module-system/src/main/resources/apiclient_key.pem");//商户私钥证书 +//// map.put("wechatPayPublicKeyId", "PUB_KEY_ID_0117123782272025033100396400002931");//商户公钥id +//// map.put("wechatPayPublicKeyFilePath", "jeecg-boot-module-system/src/main/resources/pub_key.pem");//商户公钥证书 +//// map.put("transferSceneId", "1005");//商户转账场景ID 1005-佣金报酬 +//// map.put("transferRemark", "佣金报酬");//商户转账场景ID 1005-佣金报酬 +//// map.put("userRecvPerception", "劳务报酬");//商户转账场景ID 1005-佣金报酬 +// +// //微信商户参数(瑶都万能墙测试参数) +// map.put("appid", "wxa4d29e67e8a58d38");//小程序appid +// map.put("mchid", "1673516176");//商户号 +// map.put("certiticateSerialNo", "246ED77A7F882A59FD79993D09FDD2BA9A868FFE");//商户序列号 +// map.put("privateKeyFilePath", "jeecg-boot-module-system/src/main/resources/apiclient_key_yaodu.pem");//商户私钥证书 +// map.put("wechatPayPublicKeyId", "PUB_KEY_ID_0116735161762025040100448900000949");//商户公钥id +// map.put("wechatPayPublicKeyFilePath", "jeecg-boot-module-system/src/main/resources/pub_key_yaodu.pem");//商户公钥证书 +// map.put("transferSceneId", "1005");//商户转账场景ID 1005-佣金报酬 +// map.put("transferRemark", "佣金报酬");//商户转账场景ID 1005-佣金报酬 +// map.put("userRecvPerception", "劳务报酬");//商户转账场景ID 1005-佣金报酬 +// +// +// //变化的用户信息参数 +//// map.put("openid", "oFzrW4migndUepy7zYgYO2YoZ5to");//用户openid +//// map.put("userName", "用户真实姓名");//用户真实姓名 +// map.put("transferAmount", 100L);//提现金额, 单位为“分” +// String idStr = "H" + IdWorker.getIdStr(); +// map.put("outBillNo", idStr);//商户单号 +// +// +// //转账场景报备信息,ransfer_scene_report_infos为数组类型参数,在现金营销的转账场景下需固定传两条明细,每条“转账场景报备信息明细”包含info_type、info_content两个参数。 +// map.put("infoType1","岗位类型"); +// map.put("infoContent1","外卖员"); +// map.put("infoType2","报酬说明"); +// map.put("infoContent2","高温补贴"); +// +// return map; +// +// } +// +// public static void main(String[] args) { +// // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 +// +// Map map = getMap(); +// TransferToUser client = new TransferToUser( +// map.get("mchid").toString(), // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 +// map.get("certiticateSerialNo").toString(), // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 +// map.get("privateKeyFilePath").toString(), // 商户API证书私钥文件路径,本地文件路径 +// map.get("wechatPayPublicKeyId").toString(), // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 +// map.get("wechatPayPublicKeyFilePath").toString() // 微信支付公钥文件路径,本地文件路径 +// ); +// +// TransferToUserRequest request = new TransferToUserRequest(); +// request.appid = map.get("appid").toString(); +// String idStr = "H" + IdWorker.getIdStr(); +// request.outBillNo = idStr; +// request.transferSceneId = map.get("transferSceneId").toString(); +// request.openid = map.get("openid").toString(); +// request.userName = client.encrypt(map.get("userName").toString()); +// request.transferAmount = 100L; +// request.transferRemark = map.get("transferRemark").toString(); +// request.notifyUrl = map.get("notifyUrl").toString(); +// request.userRecvPerception = map.get("userRecvPerception").toString(); +// request.transferSceneReportInfos = new ArrayList<>(); +// { +// TransferSceneReportInfo item0 = new TransferSceneReportInfo(); +// item0.infoType = map.get("infoType1").toString(); +// item0.infoContent = map.get("infoContent1").toString(); +// request.transferSceneReportInfos.add(item0); +// TransferSceneReportInfo item1 = new TransferSceneReportInfo(); +// item1.infoType = map.get("infoType2").toString(); +// item1.infoContent = map.get("infoContent2").toString(); +// request.transferSceneReportInfos.add(item1); +// }; +// try { +// TransferToUserResponse response = client.run(request, map); +// // TODO: 请求成功,继续业务逻辑 +// System.out.println(response); +// } catch (WXPayUtility.ApiException e) { +// // TODO: 请求失败,根据状态码执行不同的逻辑 +// e.printStackTrace(); +// } +// } +// +// +// public TransferToUserResponse run(Map map){ +// TransferToUser client = new TransferToUser( +// map.get("mchid").toString(), // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 +// map.get("certiticateSerialNo").toString(), // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 +// map.get("privateKeyFilePath").toString(), // 商户API证书私钥文件路径,本地文件路径 +// map.get("wechatPayPublicKeyId").toString(), // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 +// map.get("wechatPayPublicKeyFilePath").toString() // 微信支付公钥文件路径,本地文件路径 +// ); +// +// TransferToUserRequest request = new TransferToUserRequest(); +// +// +// request.appid = map.get("appid").toString(); +// request.outBillNo = map.get("outBillNo").toString(); +// request.transferSceneId = map.get("transferSceneId").toString(); +// request.openid = map.get("openid").toString(); +// request.userName = client.encrypt(map.get("userName").toString()); +// request.transferAmount = 100L; +// request.transferRemark = map.get("transferRemark").toString(); +// request.notifyUrl = map.get("notifyUrl").toString(); +// request.userRecvPerception = map.get("userRecvPerception").toString(); +// request.transferSceneReportInfos = new ArrayList<>(); +// { +// TransferSceneReportInfo item0 = new TransferSceneReportInfo(); +// item0.infoType = map.get("infoType1").toString(); +// item0.infoContent = map.get("infoContent1").toString(); +// request.transferSceneReportInfos.add(item0); +// TransferSceneReportInfo item1 = new TransferSceneReportInfo(); +// item1.infoType = map.get("infoType2").toString(); +// item1.infoContent = map.get("infoContent2").toString(); +// request.transferSceneReportInfos.add(item1); +// }; +// try { +// TransferToUserResponse response = client.run(request, map); +// // TODO: 请求成功,继续业务逻辑 +// log.info("提现发起成功,outBillNo:"+response.outBillNo + ",transferBillNo:" +response.transferBillNo + ",state:" +response.state); +// //转账结果 +// switch (response.state){ +// case ACCEPTED: +// log.info("转账已受理"); +// break; +// case PROCESSING: +// log.info("转账锁定资金中。如果一直停留在该状态,建议检查账户余额是否足够,如余额不足,可充值后再原单重试"); +// break; +// case WAIT_USER_CONFIRM: +// log.info("待收款用户确认,可拉起微信收款确认页面进行收款确认"); +// break; +// case TRANSFERING: +// log.info("转账中,可拉起微信收款确认页面再次重试确认收款"); +// break; +// case SUCCESS: +// log.info("转账成功"); +// break; +// case FAIL: +// log.info("转账失败"); +// break; +// case CANCELING: +// log.info("商户撤销请求受理成功,该笔转账正在撤销中"); +// break; +// case CANCELLED: +// log.info("转账撤销完成"); +// break; +// } +// log.info("提现发起完成"); +// return response; +// } catch (WXPayUtility.ApiException e) { +// // TODO: 请求失败,根据状态码执行不同的逻辑 +// log.info("提现发起失败"); +// e.printStackTrace(); +// return null; +// } +// } + + public TransferToUserResponse run(TransferToUserRequest request, Map map) { + String uri = map.get("path").toString(); + String host = map.get("host").toString(); + String method = map.get("method").toString(); + String reqBody = WXPayUtility.toJson(request); + + Request.Builder reqBuilder = new Request.Builder().url(host + uri); + reqBuilder.addHeader("Accept", "application/json"); + reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId); + reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, method, uri, reqBody)); + reqBuilder.addHeader("Content-Type", "application/json"); + RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody); + reqBuilder.method(method, requestBody); + Request httpRequest = reqBuilder.build(); + + // 发送HTTP请求 + OkHttpClient client = new OkHttpClient.Builder().build(); + try (Response httpResponse = client.newCall(httpRequest).execute()) { + String respBody = WXPayUtility.extractBody(httpResponse); + if (httpResponse.code() >= 200 && httpResponse.code() < 300) { + // 2XX 成功,验证应答签名 + WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey, + httpResponse.headers(), respBody); + + // 从HTTP应答报文构建返回数据 + return WXPayUtility.fromJson(respBody, TransferToUserResponse.class); + } else { + throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers()); + } + } catch (IOException e) { + throw new UncheckedIOException("Sending request to " + uri + " failed.", e); + } + } + + public String encrypt(String plainText) { + return WXPayUtility.encrypt(this.wechatPayPublicKey, plainText); + } + + public static class TransferToUserResponse { + @SerializedName("out_bill_no") + public String outBillNo; + + @SerializedName("transfer_bill_no") + public String transferBillNo; + + @SerializedName("create_time") + public String createTime; + + @SerializedName("state") + public TransferBillStatus state; + + @SerializedName("package_info") + public String packageInfo; + } + + public enum TransferBillStatus { + @SerializedName("ACCEPTED") + ACCEPTED, + @SerializedName("PROCESSING") + PROCESSING, + @SerializedName("WAIT_USER_CONFIRM") + WAIT_USER_CONFIRM, + @SerializedName("TRANSFERING") + TRANSFERING, + @SerializedName("SUCCESS") + SUCCESS, + @SerializedName("FAIL") + FAIL, + @SerializedName("CANCELING") + CANCELING, + @SerializedName("CANCELLED") + CANCELLED + } + + public static class TransferSceneReportInfo { + @SerializedName("info_type") + public String infoType; + + @SerializedName("info_content") + public String infoContent; + } + + public static class TransferToUserRequest { + @SerializedName("appid") + public String appid; + + @SerializedName("out_bill_no") + public String outBillNo; + + @SerializedName("transfer_scene_id") + public String transferSceneId; + + @SerializedName("openid") + public String openid; + + @SerializedName("user_name") + public String userName; + + @SerializedName("transfer_amount") + public Long transferAmount; + + @SerializedName("transfer_remark") + public String transferRemark; + + @SerializedName("notify_url") + public String notifyUrl; + + @SerializedName("user_recv_perception") + public String userRecvPerception; + + @SerializedName("transfer_scene_report_infos") + public List transferSceneReportInfos; + } + +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/transferUtils/WXPayUtility.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/transferUtils/WXPayUtility.java new file mode 100644 index 0000000..d9598d6 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/transferUtils/WXPayUtility.java @@ -0,0 +1,381 @@ +package org.jeecg.modules.api.transferUtils; + +import com.google.gson.*; +import com.google.gson.annotations.Expose; +import okhttp3.Headers; +import okhttp3.Response; +import okio.BufferedSource; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; +import java.util.Base64; +import java.util.Map; +import java.util.Objects; + +public class WXPayUtility { + private static final Gson gson = new GsonBuilder() + .disableHtmlEscaping() + .addSerializationExclusionStrategy(new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes fieldAttributes) { + final Expose expose = fieldAttributes.getAnnotation(Expose.class); + return expose != null && !expose.serialize(); + } + + @Override + public boolean shouldSkipClass(Class aClass) { + return false; + } + }) + .addDeserializationExclusionStrategy(new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes fieldAttributes) { + final Expose expose = fieldAttributes.getAnnotation(Expose.class); + return expose != null && !expose.deserialize(); + } + + @Override + public boolean shouldSkipClass(Class aClass) { + return false; + } + }) + .create(); + private static final char[] SYMBOLS = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + private static final SecureRandom random = new SecureRandom(); + + /** + * 将 Object 转换为 JSON 字符串 + */ + public static String toJson(Object object) { + return gson.toJson(object); + } + + /** + * 将 JSON 字符串解析为特定类型的实例 + */ + public static T fromJson(String json, Class classOfT) throws JsonSyntaxException { + return gson.fromJson(json, classOfT); + } + + /** + * 从公私钥文件路径中读取文件内容 + * + * @param keyPath 文件路径 + * @return 文件内容 + */ + private static String readKeyStringFromPath(String keyPath) { + try { + return new String(Files.readAllBytes(Paths.get(keyPath)), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * 读取 PKCS#8 格式的私钥字符串并加载为私钥对象 + * + * @param keyString 私钥文件内容,以 -----BEGIN PRIVATE KEY----- 开头 + * @return PrivateKey 对象 + */ + public static PrivateKey loadPrivateKeyFromString(String keyString) { + try { + keyString = keyString.replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + return KeyFactory.getInstance("RSA").generatePrivate( + new PKCS8EncodedKeySpec(Base64.getDecoder().decode(keyString))); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException(e); + } catch (InvalidKeySpecException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * 从 PKCS#8 格式的私钥文件中加载私钥 + * + * @param keyPath 私钥文件路径 + * @return PrivateKey 对象 + */ + public static PrivateKey loadPrivateKeyFromPath(String keyPath) { + return loadPrivateKeyFromString(readKeyStringFromPath(keyPath)); + } + + /** + * 读取 PKCS#8 格式的公钥字符串并加载为公钥对象 + * + * @param keyString 公钥文件内容,以 -----BEGIN PUBLIC KEY----- 开头 + * @return PublicKey 对象 + */ + public static PublicKey loadPublicKeyFromString(String keyString) { + try { + keyString = keyString.replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", "") + .replaceAll("\\s+", ""); + return KeyFactory.getInstance("RSA").generatePublic( + new X509EncodedKeySpec(Base64.getDecoder().decode(keyString))); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException(e); + } catch (InvalidKeySpecException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * 从 PKCS#8 格式的公钥文件中加载公钥 + * + * @param keyPath 公钥文件路径 + * @return PublicKey 对象 + */ + public static PublicKey loadPublicKeyFromPath(String keyPath) { + return loadPublicKeyFromString(readKeyStringFromPath(keyPath)); + } + + /** + * 创建指定长度的随机字符串,字符集为[0-9a-zA-Z],可用于安全相关用途 + */ + public static String createNonce(int length) { + char[] buf = new char[length]; + for (int i = 0; i < length; ++i) { + buf[i] = SYMBOLS[random.nextInt(SYMBOLS.length)]; + } + return new String(buf); + } + + /** + * 使用公钥按照 RSA_PKCS1_OAEP_PADDING 算法进行加密 + * + * @param publicKey 加密用公钥对象 + * @param plaintext 待加密明文 + * @return 加密后密文 + */ + public static String encrypt(PublicKey publicKey, String plaintext) { + final String transformation = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; + + try { + Cipher cipher = Cipher.getInstance(transformation); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8))); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new IllegalArgumentException("The current Java environment does not support " + transformation, e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("RSA encryption using an illegal publicKey", e); + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new IllegalArgumentException("Plaintext is too long", e); + } + } + + /** + * 使用私钥按照指定算法进行签名 + * + * @param message 待签名串 + * @param algorithm 签名算法,如 SHA256withRSA + * @param privateKey 签名用私钥对象 + * @return 签名结果 + */ + public static String sign(String message, String algorithm, PrivateKey privateKey) { + byte[] sign; + try { + Signature signature = Signature.getInstance(algorithm); + signature.initSign(privateKey); + signature.update(message.getBytes(StandardCharsets.UTF_8)); + sign = signature.sign(); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException("The current Java environment does not support " + algorithm, e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException(algorithm + " signature uses an illegal privateKey.", e); + } catch (SignatureException e) { + throw new RuntimeException("An error occurred during the sign process.", e); + } + return Base64.getEncoder().encodeToString(sign); + } + + /** + * 使用公钥按照特定算法验证签名 + * + * @param message 待签名串 + * @param signature 待验证的签名内容 + * @param algorithm 签名算法,如:SHA256withRSA + * @param publicKey 验签用公钥对象 + * @return 签名验证是否通过 + */ + public static boolean verify(String message, String signature, String algorithm, + PublicKey publicKey) { + try { + Signature sign = Signature.getInstance(algorithm); + sign.initVerify(publicKey); + sign.update(message.getBytes(StandardCharsets.UTF_8)); + return sign.verify(Base64.getDecoder().decode(signature)); + } catch (SignatureException e) { + return false; + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("verify uses an illegal publickey.", e); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException("The current Java environment does not support" + algorithm, e); + } + } + + /** + * 根据微信支付APIv3请求签名规则构造 Authorization 签名 + * + * @param mchid 商户号 + * @param certificateSerialNo 商户API证书序列号 + * @param privateKey 商户API证书私钥 + * @param method 请求接口的HTTP方法,请使用全大写表述,如 GET、POST、PUT、DELETE + * @param uri 请求接口的URL + * @param body 请求接口的Body + * @return 构造好的微信支付APIv3 Authorization 头 + */ + public static String buildAuthorization(String mchid, String certificateSerialNo, + PrivateKey privateKey, + String method, String uri, String body) { + String nonce = createNonce(32); + long timestamp = Instant.now().getEpochSecond(); + + String message = String.format("%s\n%s\n%d\n%s\n%s\n", method, uri, timestamp, nonce, + body == null ? "" : body); + + String signature = sign(message, "SHA256withRSA", privateKey); + + return String.format( + "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\"," + + "timestamp=\"%d\",serial_no=\"%s\"", + mchid, nonce, signature, timestamp, certificateSerialNo); + } + + /** + * 对参数进行 URL 编码 + * + * @param content 参数内容 + * @return 编码后的内容 + */ + public static String urlEncode(String content) { + try { + return URLEncoder.encode(content, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** + * 对参数Map进行 URL 编码,生成 QueryString + * + * @param params Query参数Map + * @return QueryString + */ + public static String urlEncode(Map params) { + if (params == null || params.isEmpty()) { + return ""; + } + + int index = 0; + StringBuilder result = new StringBuilder(); + for (Map.Entry entry : params.entrySet()) { + result.append(entry.getKey()) + .append("=") + .append(urlEncode(entry.getValue().toString())); + index++; + if (index < params.size()) { + result.append("&"); + } + } + return result.toString(); + } + + /** + * 从应答中提取 Body + * + * @param response HTTP 请求应答对象 + * @return 应答中的Body内容,Body为空时返回空字符串 + */ + public static String extractBody(Response response) { + if (response.body() == null) { + return ""; + } + + try { + BufferedSource source = response.body().source(); + return source.readUtf8(); + } catch (IOException e) { + throw new RuntimeException(String.format("An error occurred during reading response body. Status: %d", response.code()), e); + } + } + + /** + * 根据微信支付APIv3应答验签规则对应答签名进行验证,验证不通过时抛出异常 + * + * @param wechatpayPublicKeyId 微信支付公钥ID + * @param wechatpayPublicKey 微信支付公钥对象 + * @param headers 微信支付应答 Header 列表 + * @param body 微信支付应答 Body + */ + public static void validateResponse(String wechatpayPublicKeyId, PublicKey wechatpayPublicKey, + Headers headers, + String body) { + String timestamp = headers.get("Wechatpay-Timestamp"); + try { + Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp)); + // 拒绝过期请求 + if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) { + throw new IllegalArgumentException( + String.format("Validate http response,timestamp[%s] of httpResponse is expires, " + + "request-id[%s]", + timestamp, headers.get("Request-ID"))); + } + } catch (DateTimeException | NumberFormatException e) { + throw new IllegalArgumentException( + String.format("Validate http response,timestamp[%s] of httpResponse is invalid, " + + "request-id[%s]", timestamp, + headers.get("Request-ID"))); + } + String message = String.format("%s\n%s\n%s\n", timestamp, headers.get("Wechatpay-Nonce"), + body == null ? "" : body); + String serialNumber = headers.get("Wechatpay-Serial"); + if (!Objects.equals(serialNumber, wechatpayPublicKeyId)) { + throw new IllegalArgumentException( + String.format("Invalid Wechatpay-Serial, Local: %s, Remote: %s", wechatpayPublicKeyId, + serialNumber)); + } + String signature = headers.get("Wechatpay-Signature"); + + boolean success = verify(message, signature, "SHA256withRSA", wechatpayPublicKey); + if (!success) { + throw new IllegalArgumentException( + String.format("Validate response failed,the WechatPay signature is incorrect.%n" + + "Request-ID[%s]\tresponseHeader[%s]\tresponseBody[%.1024s]", + headers.get("Request-ID"), headers, body)); + } + } + + /** + * 微信支付API错误异常,发送HTTP请求成功,但返回状态码不是 2XX 时抛出本异常 + */ + public static class ApiException extends RuntimeException { + public final int statusCode; + public final String body; + public final Headers headers; + public ApiException(int statusCode, String body, Headers headers) { + super(String.format("微信支付API访问失败,StatusCode: %s, Body: %s", statusCode, body)); + this.statusCode = statusCode; + this.body = body; + this.headers = headers; + } + } +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/CreateOrderReq.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/CreateOrderReq.java new file mode 100644 index 0000000..78d82be --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/CreateOrderReq.java @@ -0,0 +1,8 @@ +package org.jeecg.modules.api.wxUtils; + +public class CreateOrderReq { + /** + * 微信用户openid(前端不用传参) + */ + private String wxOpenId; +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/HttpServletUtils.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/HttpServletUtils.java new file mode 100644 index 0000000..fb1b059 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/HttpServletUtils.java @@ -0,0 +1,47 @@ +package org.jeecg.modules.api.wxUtils; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + *

+ * HttpServletRequest 获取请求体 + *

+ * + * @author songfayuan + * @date 2024/9/30 19:11 + */ +public class HttpServletUtils { + + /** + * 获取请求体 + * + * @param request + * @return + * @throws IOException + */ + public static String getRequestBody(HttpServletRequest request) throws IOException { + ServletInputStream stream = null; + BufferedReader reader = null; + StringBuffer sb = new StringBuffer(); + try { + stream = request.getInputStream(); + // 获取响应 + reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } catch (IOException e) { + throw new IOException("读取返回支付接口数据流出现异常!"); + } finally { + reader.close(); + } + return sb.toString(); + } + +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/PayOrderInfo.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/PayOrderInfo.java new file mode 100644 index 0000000..5c4ce14 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/PayOrderInfo.java @@ -0,0 +1,31 @@ +package org.jeecg.modules.api.wxUtils; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + *

+ * 支付订单信息 + *

+ * + * @author songfayuan + * @date 2024/9/30 17:46 + */ +@Data +public class PayOrderInfo { + /** + * 订单标题 + */ + private String description; + /** + * 商户订单号 + * 只能是数字、大小写字母_-*且在同一个商户号下唯一。 + */ + private String outTradeNo; + /** + * 支付金额,单位:元 + */ + private BigDecimal amount; +} + diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/QueryOrderReq.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/QueryOrderReq.java new file mode 100644 index 0000000..7cceba0 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/QueryOrderReq.java @@ -0,0 +1,23 @@ +package org.jeecg.modules.api.wxUtils; + +import lombok.Data; + +/** + *

+ * 订单查询请求 + *

+ * + * @author songfayuan + * @date 2024/9/30 19:19 + */ +@Data +public class QueryOrderReq { + /** + * 订单号:业务侧的订单号 + */ + private String transactionId; + /** + * 商户订单号 + */ + private String outTradeNo; +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/RefundOrderReq.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/RefundOrderReq.java new file mode 100644 index 0000000..a54e7a8 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/RefundOrderReq.java @@ -0,0 +1,36 @@ +package org.jeecg.modules.api.wxUtils; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + *

+ * 退款订单请求参数 + *

+ * + * @author songfayuan + * @date 2024/9/30 19:19 + */ +@Data +public class RefundOrderReq { + /** + * 订单号:业务侧的订单号 + */ + private String transactionId; + /** + * 商户订单号 + */ + private String outTradeNo; + + /** + * 原订单金额 说明:原支付交易的订单总金额,这里单位为元。 + */ + private BigDecimal totalAmount; + + /** + * 退款金额 说明:退款金额,这里单位为元,不能超过原订单支付金额。 + */ + private BigDecimal refundAmount; +} + diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/Response.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/Response.java new file mode 100644 index 0000000..05968e8 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/Response.java @@ -0,0 +1,164 @@ +package org.jeecg.modules.api.wxUtils; + +import lombok.Data; + +/** + * 返回参数封装类 + * + * @param + * @author songfayuan + */ + +@Data +public class Response { + /** + * 状态码 + */ + protected int code; + + /** + * 提示信息 + */ + protected String msg; + + /** + * 数据 + */ + protected T data; + + /** + * 成功时状态码 + */ + private static final int SUCCESS_CODE = 200; + + /** + * 成功时提示信息 + */ + private static final String SUCCESS_MSG = "success"; + + /** + * 异常、错误信息状态码 + */ + private static final int ERROR_CODE = 500; + + /** + * 异常、错误提示信息 + */ + private static final String ERROR_MSG = "服务器内部异常,请联系技术人员!"; + + /** + * 返回成功消息 + * + * @param + * @return + */ + public static Response success() { + Response resp = new Response(); + resp.code = (SUCCESS_CODE); + resp.msg = (SUCCESS_MSG); + return resp; + } + + /** + * 返回成功消息 + * + * @param + * @param msg + * @return + */ + public static Response successResponse(String msg) { + Response resp = new Response(); + resp.code = SUCCESS_CODE; + resp.msg = msg; + return resp; + } + + /** + * 返回错误消息 + * + * @param + * @return + */ + public static Response error() { + Response resp = new Response(); + resp.code = (ERROR_CODE); + resp.msg = (ERROR_MSG); + return resp; + } + + /** + * 返回错误消息 + * + * @param + * @param msg + * @return + */ + public static Response errorResponse(String msg) { + Response resp = new Response(); + resp.code = ERROR_CODE; + resp.msg = msg; + return resp; + } + + /** + * 自定义状态码、提示信息 + * + * @param + * @param code 状态码 + * @param msg 提示信息 + * @return + */ + public static Response response(int code, String msg) { + Response resp = new Response(); + resp.code = (code); + resp.msg = (msg); + return resp; + } + + /** + * 自定义状态码、提示信息、业务数据 + * + * @param + * @param code 状态码 + * @param msg 提示信息 + * @param data 业务数据 + * @return + */ + public static Response response(int code, String msg, T data) { + Response resp = new Response<>(); + resp.code = (code); + resp.msg = (msg); + resp.data = data; + return resp; + } + + /** + * 返回成功数据 + * + * @param + * @param data 业务数据 + * @return + */ + public static Response success(T data) { + Response resp = new Response<>(); + resp.code = (SUCCESS_CODE); + resp.msg = (SUCCESS_MSG); + resp.data = data; + return resp; + } + + /** + * 返回错误消息 + * + * @param + * @param data 业务数据 + * @return + */ + public static Response error(T data) { + Response resp = new Response<>(); + resp.code = (ERROR_CODE); + resp.msg = (ERROR_MSG); + resp.data = data; + return resp; + } +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/WxPayAutoCertificateConfig.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/WxPayAutoCertificateConfig.java new file mode 100644 index 0000000..8575145 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/WxPayAutoCertificateConfig.java @@ -0,0 +1,88 @@ +package org.jeecg.modules.api.wxUtils; + + +import com.wechat.pay.java.core.Config; +import com.wechat.pay.java.core.RSAPublicKeyConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@Configuration +public class WxPayAutoCertificateConfig { + + /** + * 商户号 + */ + @Value("${wx.miniapp.merchantId}") + private String merchantId; + + /** + * 商户API私钥路径 + */ + @Value("${wx.miniapp.privateKeyPath}") + private String privateKeyPath; + + /** + * 商户API公钥路径 + */ + @Value("${wx.miniapp.publicKeyPath}") + private String publicKeyPath; + + /** + * 公钥 + */ + @Value("${wx.miniapp.publicKeyId}") + private String publicKeyId; + + /** + * 商户证书序列号 + */ + @Value("${wx.miniapp.merchantSerialNumber}") + private String merchantSerialNumber; + + /** + * 商户APIV3密钥 + */ + @Value("${wx.miniapp.apiV3Key}") + private String apiV3Key; + + /** + * 初始化商户配置 + * @return + */ + @Bean + public Config rsaAutoCertificateConfig() { + // 这里把Config作为配置Bean是为了避免多次创建资源,一般项目运行的时候这些东西都确定了 + // 具体的参数改为申请的数据,可以通过读配置文件的形式获取 +// Config config = new RSAAutoCertificateConfig.Builder() +// .merchantId("1659066870") +// .privateKeyFromPath("E:\\git_java\\api_java\\popularize-admin\\popularize-admin\\popularize-admin\\module-pay\\src\\main\\resources\\apiclient_key.pem") +// .merchantSerialNumber("7BE56DC695B2B612BD1C6C710A7FBFA1AC46B10F") +// .apiV3Key("vtribevtribevtribevtribevtribe12") +// .build(); + + Config config = new RSAPublicKeyConfig.Builder() + .merchantId(merchantId) + .privateKeyFromPath(privateKeyPath) + .publicKeyFromPath(publicKeyPath) + .publicKeyId(publicKeyId) + .merchantSerialNumber(merchantSerialNumber) + .apiV3Key(apiV3Key) + .build(); + + +// Config config = new RSAAutoCertificateConfig.Builder() +// .merchantId(merchantId) +// .privateKeyFromPath(privateKeyPath) +// .merchantSerialNumber(merchantSerialNumber) +// .apiV3Key(apiV3Key) +// .build(); + + log.info("初始化微信支付商户配置完成..."); + return config; + } + + +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/WxPayConfig.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/WxPayConfig.java new file mode 100644 index 0000000..93b73ac --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/wxUtils/WxPayConfig.java @@ -0,0 +1,13 @@ +package org.jeecg.modules.api.wxUtils; +import lombok.Data; +import org.springframework.stereotype.Component; + + +@Data +@Component +public class WxPayConfig { + + + +} + diff --git a/jeecg-boot-module-system/src/main/resources/apiclient_key.pem b/jeecg-boot-module-system/src/main/resources/apiclient_key.pem new file mode 100644 index 0000000..a9c2b1e --- /dev/null +++ b/jeecg-boot-module-system/src/main/resources/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsmLqcklIEJ5z3 +szJnxQo2/j/qhSFv/JpQsVw8NW3ueGFtwKcKUJysytsBxwkHvJR9wWXpmf+NCaG3 +bcPnNs9TIH1MRafuEwxHnaJD6378/Jd/Fq9TV1y86p8mciOn3vYNwwZKiIpsGYGz +UOcM/TYYl6iIBdkO+fjgghLURhubaf4E4bTbmzkQARgKCCdx4d+/SOLQJwICXGjt +a/XJnuHGxSATHjq31AhToTjR5MtQILNZLHz313e/MP6VI6yYMySXFNXotb8lUjNN +xUZv7morU6esNRdcSdVjibD7CfrO0bPr5MyyTGKZYCgXzcVkoEsDJ127kwKqWB9Y +fLQ7OhIFAgMBAAECggEATCpmB5xGl1DH40NwFf0NIfGXUS4I8Ht/ueZa9rL5XUhA +boea72Fdkp8+gI1N/d0JhmlnSOxNqva6J9FHp2w3l855nMQMdHJxc9Ure1+cPZZW +YJ4pJtUhtrivHO83rugkmzQaPZ/9vfCPnQsxuziuu7GJjOVPwV0sqB6eopCBtFYa +zrWjO9eDT74SQh12q1UmjYhRuAF7pnn8pLLnl4rtgfYtJE2mjT7GvQ6j9j/EVO+N +xeDG7VlWQfdi3I/Hl0fk8GLWyw+S3GGy3ZG+A4MnGpDuN7hGv4R1jplXigGLVOYu +xoMbC4oM/4vna0u+i9m33d8DN6vC3AbgqB8XYly8YQKBgQDjLZ5vK+/d7eE0bmpE +sA/P3e7ZJofwFnPLMGaNaQF4MdBu4teOVoiU4nwx0ir2EeD55e60Bf8EbNYwUrJ+ +m+taW0HjhF+PlxbEBiy8RXH+sMzBH+et+eEoJmqZKRf5V+PBanreWDB4FZkV8zHS +g/CL6MUfA7JL01AvQMIUn0KJKQKBgQDCfmNFiA9sT+t8rb0XBPQUgzTHWgQo0jur +rHjyRilSav4i0Ef+WQP2+s2LuywBg10veZMFCawEt48xC4L6ot+8fk1fhD5pKbCK +XQ5MLN2bJcNFZakMlcO+CeyTPr/reRf12GaG7o9fdUeG6/T0N8aqgkAsjGzL6Ky9 +2bdaS+JxfQKBgF8lZA27oSKZ1pBXDjrmYjj1AmwALz7mqGiZRNpE+FCWzG0kpJMA +uwNgOBPFqiiVJFyFaIwNVtlLnAlxGUDdnJyjS902O6+v3+EJlQLlMvEwqxDXg/jh +huOB83NiOqA7pzACoKMu3F3u7ca7rEo26DAwtR5gNfR79EFWJfLtILjBAoGBAJ1s +m4GOLZ2n2p2iKulU8xMfWDr7m15HPFEqWwMO0yeIvocDOyKefl0MiA7Pw0C+SVDC +AMQVHgz7XoV+IdgouRvix1HGwpVwTxrPU2FI91465KFpNbmgBz3s3nYYVlOMQl6V +d/dDZP0I+oo0tqJt+vnMT5E/YFhzzk4kyQnnXjQpAoGBAJcpk9hEuzfhWWuKeydB +Pwbnxn4MwyLg0dCiPeDJORnzEhCETcRjWBkNrT1vOcd3zeEhrdPDnBzdOAFq/Vqd +Os7nYTRESXS6m/czHQozpbrP2nuOkunN1WgT15Un6WxQAcIy4uPIbtlkV1pJGkaW +pejfCZZ++YLveAjsp9fIBmJo +-----END PRIVATE KEY----- diff --git a/jeecg-boot-module-system/src/main/resources/application-dev.yml b/jeecg-boot-module-system/src/main/resources/application-dev.yml index 81d0621..9b7f539 100644 --- a/jeecg-boot-module-system/src/main/resources/application-dev.yml +++ b/jeecg-boot-module-system/src/main/resources/application-dev.yml @@ -329,3 +329,16 @@ third-app: client-secret: ?? agent-id: ?? +## 微信商户 +wx: + miniapp: + appid: wxee64675d48680dd4 #微信小程序appid + secret: d7e3f49858350cf40b1aeeb8fa1c61eb #微信小程序密钥 + merchantId: 1722176565 # 商户号 + privateKeyPath: jeecg-boot-module-system/src/main/resources/apiclient_key.pem #本地私钥路径 + publicKeyPath: jeecg-boot-module-system/src/main/resources/pub_key.pem #本地公钥路径 +# privateKeyPath: /root/massage/cerFile/apiclient_key.pem #线上私钥路径 +# publicKeyPath: /root/massage/cerFile/pub_key.pem #线上公钥路径 + publicKeyId: daihuoqu #公钥 + merchantSerialNumber: daihuoqu # 商户API证书序列号 + apiV3Key: 0fdb77429ffdf206c151af76a663041c # 商户APIV3密钥 \ No newline at end of file diff --git a/jeecg-boot-module-system/src/main/resources/pub_key.pem b/jeecg-boot-module-system/src/main/resources/pub_key.pem new file mode 100644 index 0000000..bd7b4f5 --- /dev/null +++ b/jeecg-boot-module-system/src/main/resources/pub_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6xCzq1CjrZYTqtE52f7t +4AU9rgRZYKNn94tmkIuYa1wGX13gWNIlo1muExV/Ejg4H9Xe0RCQwNNJieeyfhU7 +Vl/qms4Ukj8sqIrvKqw9j81AVHK2BSk87aRs0ha+gvV1tDDmcCLHrY8SeGcJ2U2z +I9iu5X1kLdkXNiojWrMngGslki2FSfJelftoZUDk30mf18AoG5iBzIPJ4/Gy5Rh0 +U+FDwpWzERR7XjMoAZ7YiRYUdV/SpQkx6CrM4Em3AKxmpxvwSVg7NiMDoTljfgq+ +6ZHB9DMQPbeqkQ9qkCMg0sVjpB/aI9tObE0mtU8zP/mBg7TDTs0qa2SXFem5ZTDg +JQIDAQAB +-----END PUBLIC KEY-----