From 999720d16ebd2df4211dea0a7a1feee976d5ae68 Mon Sep 17 00:00:00 2001 From: "925116093-qq.com" <925116093@qq.com> Date: Fri, 27 Jun 2025 18:17:38 +0800 Subject: [PATCH] 202506271817 --- .../ruoyi/system/controller/CursorUtil.java | 164 ++++++++++++++ .../controllerUtil/WechatPayV3Util.java | 204 ++++++++++++++++++ ruoyi-ui/src/api/system/dict/info.js | 44 ---- ruoyi-ui/src/api/system/{dict => }/points.js | 0 4 files changed, 368 insertions(+), 44 deletions(-) create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/controller/CursorUtil.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WechatPayV3Util.java delete mode 100644 ruoyi-ui/src/api/system/dict/info.js rename ruoyi-ui/src/api/system/{dict => }/points.js (100%) diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/CursorUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/CursorUtil.java new file mode 100644 index 0000000..e7275f7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/CursorUtil.java @@ -0,0 +1,164 @@ +package com.ruoyi.system.controller; + +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.system.ControllerUtil.*; +import com.ruoyi.system.domain.*; +import com.ruoyi.system.service.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 游标工具控制器 + * + * @author Mr. Zhang Pan + * @version 1.0 + * @date 2025-01-10 + */ +@RestController +public class CursorUtil extends BaseController { + + @Autowired + private IServiceGoodsService serviceGoodsService; + + @Autowired + private IGoodsOrderCursorService goodsOrderCursorService; + + @Autowired + private IUsersService usersService; + + @Autowired + private IUserAddressService userAddressService; + + @Autowired + private IGoodsOrderService goodsOrderService; + + @Autowired + private WechatPayUtil wechatPayUtil; + + @Autowired + private IOrderService orderService; + + @Autowired + private IOrderLogService orderLogService; + + @Autowired + private IOrderCommentService orderCommentService; + + @Autowired + private IUsersInvoiceInfoService usersInvoiceInfoService; + + /** + * 小程序接口:维护用户发票信息(新增/修改) + * POST /api/user/invoice/info + * @param params 发票信息参数 + * @param request 请求对象 + * @return AjaxResult + */ + @PostMapping("/api/user/invoice/info") + public AjaxResult saveOrUpdateInvoiceInfo(@RequestBody Map params, HttpServletRequest request) { + // 1. 校验用户登录 + String token = request.getHeader("token"); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); + if (!(Boolean) userValidation.get("valid")) { + return AjaxResult.error("用户未登录或token无效"); + } + Users user = (Users) userValidation.get("user"); + if (user == null) { + return AjaxResult.error("用户信息获取失败"); + } + // 2. 构建发票对象 + UsersInvoiceInfo invoiceInfo = new UsersInvoiceInfo(); + invoiceInfo.setId(params.get("id") != null ? Integer.valueOf(params.get("id").toString()) : null); + invoiceInfo.setUid(user.getId() != null ? user.getId().intValue() : null); + invoiceInfo.setInvoiceTitle((String) params.get("invoiceTitle")); + invoiceInfo.setTaxNumber((String) params.get("taxNumber")); + invoiceInfo.setBankName((String) params.get("bankName")); + invoiceInfo.setBankAccount((String) params.get("bankAccount")); + invoiceInfo.setAddress((String) params.get("address")); + invoiceInfo.setPhone((String) params.get("phone")); + invoiceInfo.setEmail((String) params.get("email")); + invoiceInfo.setWechat((String) params.get("wechat")); + invoiceInfo.setType(params.get("type") != null ? Integer.valueOf(params.get("type").toString()) : 1); + invoiceInfo.setCategory(params.get("category") != null ? Integer.valueOf(params.get("category").toString()) : 1); + // 3. 保存或更新 + try { + if (invoiceInfo.getId() != null) { + usersInvoiceInfoService.updateUsersInvoiceInfo(invoiceInfo); + } else { + usersInvoiceInfoService.insertUsersInvoiceInfo(invoiceInfo); + } + return AjaxResult.success("发票信息保存成功"); + } catch (Exception e) { + return AjaxResult.error("发票信息保存失败: " + e.getMessage()); + } + } + + /** + * 小程序接口:查询当前用户所有发票信息 + * GET /api/user/invoice/list + * @param request 请求对象 + * @return AjaxResult + */ + @GetMapping("/api/user/invoice/list") + public AjaxResult getInvoicelist(HttpServletRequest request) { + // 1. 校验用户登录 + String token = request.getHeader("token"); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); + if (!(Boolean) userValidation.get("valid")) { + return AjaxResult.error("用户未登录或token无效"); + } + Users user = (Users) userValidation.get("user"); + if (user == null) { + return AjaxResult.error("用户信息获取失败"); + } + UsersInvoiceInfo invoiceInfo = new UsersInvoiceInfo(); + invoiceInfo.setUid(user.getId() != null ? user.getId().intValue() : null); + // 2. 查询所有发票信息 + List list = usersInvoiceInfoService.selectUsersInvoiceInfoList(invoiceInfo); + return AjaxResult.success(list); + } + + /** + * 小程序接口:删除用户单个发票信息 + * DELETE /api/user/invoice/delete/{id} + * @param id 发票ID + * @param request 请求对象 + * @return AjaxResult + */ + @DeleteMapping("/api/user/invoice/delete/{id}") + public AjaxResult deleteInvoiceInfo(@PathVariable Integer id, HttpServletRequest request) { + // 1. 校验用户登录 + String token = request.getHeader("token"); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); + if (!(Boolean) userValidation.get("valid")) { + return AjaxResult.error("用户未登录或token无效"); + } + Users user = (Users) userValidation.get("user"); + if (user == null) { + return AjaxResult.error("用户信息获取失败"); + } + // 2. 删除发票(需校验归属) + try { + int result = usersInvoiceInfoService.deleteUsersInvoiceInfoById(id); + if (result > 0) { + return AjaxResult.success("删除成功"); + } else { + return AjaxResult.error("删除失败或无权限"); + } + } catch (Exception e) { + return AjaxResult.error("删除异常: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WechatPayV3Util.java b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WechatPayV3Util.java new file mode 100644 index 0000000..94f5e61 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WechatPayV3Util.java @@ -0,0 +1,204 @@ +package com.ruoyi.system.ControllerUtil; + +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.system.config.WechatConfig; +import com.ruoyi.common.utils.spring.SpringUtils; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.*; + +/** + * 微信支付v3工具类(小程序支付/退款/提现) + * 你需要在WechatConfig中补充apiv3Key、privateKeyPath、serialNo等配置,并实现v3签名算法。 + * 所有v3接口均需用商户API私钥签名,回包敏感字段需用apiv3Key解密。 + * 详细文档见:https://pay.weixin.qq.com/wiki/doc/apiv3/ + */ +@Component +public class WechatPayV3Util { + + private static final RestTemplate restTemplate = createRestTemplate(); + + private static RestTemplate createRestTemplate() { + RestTemplate template = new RestTemplate(); + template.getMessageConverters().forEach(converter -> { + if (converter instanceof org.springframework.http.converter.StringHttpMessageConverter) { + ((org.springframework.http.converter.StringHttpMessageConverter) converter) + .setDefaultCharset(java.nio.charset.StandardCharsets.UTF_8); + } + }); + return template; + } + + private static WechatConfig wechatConfig() { + return SpringUtils.getBean(WechatConfig.class); + } + + /** + * v3 小程序支付下单(JSAPI) + * @param openid 用户openid + * @param orderNo 商户订单号 + * @param totalFee 支付金额(分) + * @param body 商品描述 + * @param notifyUrl 回调通知地址 + * @return 下单结果,包含前端调起支付的参数 + */ + public Map jsapiPay(String openid, String orderNo, int totalFee, String body, String notifyUrl) { + Map result = new HashMap<>(); + try { + String appid = wechatConfig().getAppid(); + String mchid = wechatConfig().getMchid(); + // TODO: 你需要在WechatConfig中补充apiv3Key、privateKeyPath、serialNo等配置 + Map amount = new HashMap<>(); + amount.put("total", totalFee); + amount.put("currency", "CNY"); + Map payer = new HashMap<>(); + payer.put("openid", openid); + Map params = new HashMap<>(); + params.put("appid", appid); + params.put("mchid", mchid); + params.put("description", body); + params.put("out_trade_no", orderNo); + params.put("notify_url", notifyUrl); + params.put("amount", amount); + params.put("payer", payer); + String bodyJson = new JSONObject(params).toJSONString(); + + // 生成v3签名串,构造Authorization头 + // TODO: 你需要实现v3签名算法,生成Authorization头 + String authorization = "请实现v3签名算法,生成Authorization头"; + + String v3Url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", authorization); + headers.set("Content-Type", "application/json"); + headers.set("Accept", "application/json"); + HttpEntity requestEntity = new HttpEntity<>(bodyJson, headers); + ResponseEntity response = restTemplate.exchange(v3Url, HttpMethod.POST, requestEntity, String.class); + + // 解析响应,获取prepay_id + // TODO: 你需要根据微信v3返回结构处理,部分字段加密需用apiv3Key解密 + result.put("success", true); + result.put("response", response.getBody()); + result.put("message", "v3下单接口请求已发送,请根据返回内容进一步处理"); + } catch (Exception e) { + result.put("success", false); + result.put("message", "v3下单异常:" + e.getMessage()); + } + return result; + } + + /** + * v3 申请退款 + * @param orderNo 商户订单号 + * @param refundNo 退款单号 + * @param totalFee 订单总金额(分) + * @param refundFee 退款金额(分) + * @param reason 退款原因 + * @param notifyUrl 退款回调通知地址 + * @return 退款结果 + */ + public Map refund(String orderNo, String refundNo, int totalFee, int refundFee, String reason, String notifyUrl) { + Map result = new HashMap<>(); + try { + String appid = wechatConfig().getAppid(); + String mchid = wechatConfig().getMchid(); + // TODO: 你需要在WechatConfig中补充apiv3Key、privateKeyPath、serialNo等配置 + Map amount = new HashMap<>(); + amount.put("refund", refundFee); + amount.put("total", totalFee); + amount.put("currency", "CNY"); + Map params = new HashMap<>(); + params.put("out_trade_no", orderNo); + params.put("out_refund_no", refundNo); + params.put("reason", reason); + params.put("notify_url", notifyUrl); + params.put("amount", amount); + String bodyJson = new JSONObject(params).toJSONString(); + + // 生成v3签名串,构造Authorization头 + // TODO: 你需要实现v3签名算法,生成Authorization头 + String authorization = "请实现v3签名算法,生成Authorization头"; + + String v3Url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", authorization); + headers.set("Content-Type", "application/json"); + headers.set("Accept", "application/json"); + HttpEntity requestEntity = new HttpEntity<>(bodyJson, headers); + ResponseEntity response = restTemplate.exchange(v3Url, HttpMethod.POST, requestEntity, String.class); + + // 解析响应 + // TODO: 你需要根据微信v3返回结构处理,部分字段加密需用apiv3Key解密 + result.put("success", true); + result.put("response", response.getBody()); + result.put("message", "v3退款接口请求已发送,请根据返回内容进一步处理"); + } catch (Exception e) { + result.put("success", false); + result.put("message", "v3退款异常:" + e.getMessage()); + } + return result; + } + + /** + * v3 商户提现(转账到零钱) + * @param openid 用户openid + * @param amount 金额(分) + * @param desc 提现描述 + * @return 提现结果 + */ + public Map withdraw(String openid, int amount, String desc) { + Map result = new HashMap<>(); + try { + String appid = wechatConfig().getAppid(); + String mchid = wechatConfig().getMchid(); + // TODO: 你需要在WechatConfig中补充apiv3Key、privateKeyPath、serialNo等配置 + String outBatchNo = "BATCH" + System.currentTimeMillis(); + String batchName = "商户提现"; + String batchRemark = desc; + String detailNo = "DETAIL" + System.currentTimeMillis(); + Map detail = new HashMap<>(); + detail.put("out_detail_no", detailNo); + detail.put("transfer_amount", amount); + detail.put("transfer_remark", desc); + detail.put("openid", openid); + List> detailList = new ArrayList<>(); + detailList.add(detail); + Map params = new HashMap<>(); + params.put("appid", appid); + params.put("out_batch_no", outBatchNo); + params.put("batch_name", batchName); + params.put("batch_remark", batchRemark); + params.put("total_amount", amount); + params.put("total_num", 1); + params.put("transfer_detail_list", detailList); + String bodyJson = new JSONObject(params).toJSONString(); + + // 生成v3签名串,构造Authorization头 + // TODO: 你需要实现v3签名算法,生成Authorization头 + String authorization = "请实现v3签名算法,生成Authorization头"; + + String v3Url = "https://api.mch.weixin.qq.com/v3/transfer/batches"; + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", authorization); + headers.set("Content-Type", "application/json"); + headers.set("Accept", "application/json"); + HttpEntity requestEntity = new HttpEntity<>(bodyJson, headers); + ResponseEntity response = restTemplate.exchange(v3Url, HttpMethod.POST, requestEntity, String.class); + + // 解析响应 + // TODO: 你需要根据微信v3返回结构处理,部分字段加密需用apiv3Key解密 + result.put("success", true); + result.put("response", response.getBody()); + result.put("message", "v3提现接口请求已发送,请根据返回内容进一步处理"); + } catch (Exception e) { + result.put("success", false); + result.put("message", "v3提现异常:" + e.getMessage()); + } + return result; + } +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/dict/info.js b/ruoyi-ui/src/api/system/dict/info.js deleted file mode 100644 index bb0c411..0000000 --- a/ruoyi-ui/src/api/system/dict/info.js +++ /dev/null @@ -1,44 +0,0 @@ -import request from '@/utils/request' - -// 查询发票信息列表 -export function listInfo(query) { - return request({ - url: '/system/info/list', - method: 'get', - params: query - }) -} - -// 查询发票信息详细 -export function getInfo(id) { - return request({ - url: '/system/info/' + id, - method: 'get' - }) -} - -// 新增发票信息 -export function addInfo(data) { - return request({ - url: '/system/info', - method: 'post', - data: data - }) -} - -// 修改发票信息 -export function updateInfo(data) { - return request({ - url: '/system/info', - method: 'put', - data: data - }) -} - -// 删除发票信息 -export function delInfo(id) { - return request({ - url: '/system/info/' + id, - method: 'delete' - }) -} diff --git a/ruoyi-ui/src/api/system/dict/points.js b/ruoyi-ui/src/api/system/points.js similarity index 100% rename from ruoyi-ui/src/api/system/dict/points.js rename to ruoyi-ui/src/api/system/points.js