From 8f96699299e3cf8f4a9a27b2e81478b7520ea971 Mon Sep 17 00:00:00 2001 From: pengren Date: Mon, 15 Apr 2024 11:47:31 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E4=BF=AE=E6=94=B9=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/controller/OrderInfoController.java | 20 +++++++--------- ...rderListreqVo.java => OrderListReqVo.java} | 2 +- .../sf/order/domain/res/OrderListResVo.java | 4 ++++ .../com/sf/order/mapper/OrderInfoMapper.java | 4 ++-- .../sf/order/service/IOrderInfoService.java | 4 ++-- .../service/impl/OrderInfoServiceImpl.java | 4 ++-- .../mapper/order/OrderInfoMapper.xml | 24 +++++++++++++++---- 7 files changed, 39 insertions(+), 23 deletions(-) rename sf-admin/src/main/java/com/sf/order/domain/req/{OrderListreqVo.java => OrderListReqVo.java} (89%) diff --git a/sf-admin/src/main/java/com/sf/order/controller/OrderInfoController.java b/sf-admin/src/main/java/com/sf/order/controller/OrderInfoController.java index c1e356d..d887548 100644 --- a/sf-admin/src/main/java/com/sf/order/controller/OrderInfoController.java +++ b/sf-admin/src/main/java/com/sf/order/controller/OrderInfoController.java @@ -3,12 +3,14 @@ package com.sf.order.controller; import com.sf.common.annotation.Log; import com.sf.common.core.controller.BaseController; import com.sf.common.core.domain.AjaxResult; +import com.sf.common.core.domain.entity.SysUser; import com.sf.common.core.page.TableDataInfo; import com.sf.common.enums.BusinessType; +import com.sf.common.utils.SecurityUtils; import com.sf.common.utils.poi.ExcelUtil; import com.sf.order.domain.OrderInfo; import com.sf.order.domain.dto.OrderCreateDto; -import com.sf.order.domain.req.OrderListreqVo; +import com.sf.order.domain.req.OrderListReqVo; import com.sf.order.domain.res.OrderListResVo; import com.sf.order.service.IOrderInfoService; import io.swagger.v3.oas.annotations.Operation; @@ -35,21 +37,15 @@ public class OrderInfoController extends BaseController { * 查询订单基础信息列表 */ @GetMapping("/list") - public TableDataInfo list(OrderInfo orderInfo) { + public TableDataInfo list(OrderListReqVo vo) { startPage(); - List list = orderInfoService.selectOrderInfoList(orderInfo); + SysUser user = SecurityUtils.getLoginUser().getUser(); + vo.setUserId(user.getUserId()); + List list = orderInfoService.queryList(vo); + logger.info("prderList"+list); return getDataTable(list); } - /** - * 查询订单基础信息列表 - */ - @GetMapping("/query/list") - public TableDataInfo queryList(OrderListreqVo vo) { - startPage(); - List list = orderInfoService.queryList(vo); - return getDataTable(list); - } /** * 导出订单基础信息列表 diff --git a/sf-admin/src/main/java/com/sf/order/domain/req/OrderListreqVo.java b/sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java similarity index 89% rename from sf-admin/src/main/java/com/sf/order/domain/req/OrderListreqVo.java rename to sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java index 52b41c9..98b755a 100644 --- a/sf-admin/src/main/java/com/sf/order/domain/req/OrderListreqVo.java +++ b/sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java @@ -3,7 +3,7 @@ package com.sf.order.domain.req; import lombok.Data; @Data -public class OrderListreqVo { +public class OrderListReqVo { /** * 主键id */ diff --git a/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java b/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java index b5cda3e..7d13bd1 100644 --- a/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java +++ b/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java @@ -38,6 +38,10 @@ public class OrderListResVo { * 商品标题 */ private String productTitle; + /** + * 图片 + */ + private String productPicture; /** * 商品规格 diff --git a/sf-admin/src/main/java/com/sf/order/mapper/OrderInfoMapper.java b/sf-admin/src/main/java/com/sf/order/mapper/OrderInfoMapper.java index b4f9f31..dfb2950 100644 --- a/sf-admin/src/main/java/com/sf/order/mapper/OrderInfoMapper.java +++ b/sf-admin/src/main/java/com/sf/order/mapper/OrderInfoMapper.java @@ -1,7 +1,7 @@ package com.sf.order.mapper; import com.sf.order.domain.OrderInfo; -import com.sf.order.domain.req.OrderListreqVo; +import com.sf.order.domain.req.OrderListReqVo; import com.sf.order.domain.res.OrderListResVo; import java.util.List; @@ -31,7 +31,7 @@ public interface OrderInfoMapper { public List selectOrderInfoList(OrderInfo orderInfo); - List queryList(OrderListreqVo vo); + List queryList(OrderListReqVo vo); /** * 新增订单基础信息 diff --git a/sf-admin/src/main/java/com/sf/order/service/IOrderInfoService.java b/sf-admin/src/main/java/com/sf/order/service/IOrderInfoService.java index c63358f..26210aa 100644 --- a/sf-admin/src/main/java/com/sf/order/service/IOrderInfoService.java +++ b/sf-admin/src/main/java/com/sf/order/service/IOrderInfoService.java @@ -3,7 +3,7 @@ package com.sf.order.service; import com.sf.order.domain.OrderInfo; import com.sf.order.domain.dto.OrderCreateDto; -import com.sf.order.domain.req.OrderListreqVo; +import com.sf.order.domain.req.OrderListReqVo; import com.sf.order.domain.res.OrderListResVo; import java.util.List; @@ -35,7 +35,7 @@ public interface IOrderInfoService /** * 查询订单基础信息列表 */ - List queryList(OrderListreqVo vo); + List queryList(OrderListReqVo vo); /** * 新增订单基础信息 diff --git a/sf-admin/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java b/sf-admin/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java index 834cb65..5e3edda 100644 --- a/sf-admin/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java +++ b/sf-admin/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java @@ -5,7 +5,7 @@ import com.sf.common.utils.DateUtils; import com.sf.common.utils.SnowflakeIdWorker; import com.sf.order.domain.OrderInfo; import com.sf.order.domain.dto.OrderCreateDto; -import com.sf.order.domain.req.OrderListreqVo; +import com.sf.order.domain.req.OrderListReqVo; import com.sf.order.domain.res.OrderListResVo; import com.sf.order.mapper.OrderInfoMapper; import com.sf.order.service.IOrderInfoService; @@ -51,7 +51,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService { return orderInfoMapper.selectOrderInfoList(orderInfo); } - public List queryList(OrderListreqVo vo) + public List queryList(OrderListReqVo vo) { return orderInfoMapper.queryList(vo); } diff --git a/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml b/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml index e9141e7..e2a305d 100644 --- a/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml +++ b/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml @@ -30,12 +30,23 @@ + + + + + + + + + + select id, order_no, order_status, pay_type, pay_channel, order_amt, freight_amt, pay_amt, really_amt, receive_type, goods_id, business_id, receive_addr_id, create_time, pay_time, create_user_id, update_user_id, is_delete, update_time, track_no, order_type, out_order_no, pay_data, reduction_amout from ORDER_INFO - select id, order_no, order_status, pay_type, productTitle, order_amt, freight_amt, pay_amt, really_amt, receive_type, goods_id, business_id, receive_addr_id, create_time, pay_time, create_user_id, update_user_id, is_delete, update_time, track_no, order_type, out_order_no, pay_data, reduction_amout from ORDER_INFO + SELECT a.order_no,a.order_status,a.order_amt,b.product_title,b.product_picture,b.product_desc,b.goods_spec + FROM ORDER_INFO a LEFT JOIN GOODS_MESSAGES b ON a.goods_id = b.id + + - insert into ORDER_INFO From 2f77f047a5d56a7b5cb6312830f7ddebf9f7b9ea Mon Sep 17 00:00:00 2001 From: pengren Date: Mon, 15 Apr 2024 13:50:51 +0800 Subject: [PATCH 02/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E8=AE=A2=E5=8D=95=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/sf/order/domain/req/OrderListReqVo.java | 6 +++++- .../main/java/com/sf/order/domain/res/OrderListResVo.java | 2 +- .../src/main/resources/mapper/order/OrderInfoMapper.xml | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java b/sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java index 98b755a..c0e0d17 100644 --- a/sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java +++ b/sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java @@ -1,9 +1,13 @@ package com.sf.order.domain.req; +import com.sf.common.core.domain.BaseEntity; import lombok.Data; @Data -public class OrderListReqVo { +public class OrderListReqVo extends BaseEntity { + + private static final long serialVersionUID = 1L; + /** * 主键id */ diff --git a/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java b/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java index 7d13bd1..1c7a63d 100644 --- a/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java +++ b/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java @@ -46,7 +46,7 @@ public class OrderListResVo { /** * 商品规格 */ - private Integer goodsSpec; + private String goodsSpec; /** * 商品数量 */ diff --git a/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml b/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml index e2a305d..207b139 100644 --- a/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml +++ b/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml @@ -44,7 +44,7 @@ select id, order_no, order_status, pay_type, pay_channel, order_amt, freight_amt, pay_amt, really_amt, receive_type, goods_id, business_id, receive_addr_id, create_time, pay_time, create_user_id, update_user_id, is_delete, update_time, track_no, order_type, out_order_no, pay_data, reduction_amout from ORDER_INFO - + SELECT a.order_no,a.order_status,a.order_amt,b.product_title,b.product_picture,b.product_desc,b.goods_spec FROM ORDER_INFO a LEFT JOIN GOODS_MESSAGES b ON a.goods_id = b.id @@ -77,10 +77,10 @@ From b5d583e9bc71edb207f23759162a92a319603ab7 Mon Sep 17 00:00:00 2001 From: pengren Date: Tue, 16 Apr 2024 09:45:04 +0800 Subject: [PATCH 03/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/sf/file/controller/SysOssController.java | 1 + sf-file/src/main/java/com/sf/file/domain/SysOss.java | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java index 55b91c5..379ec92 100644 --- a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java +++ b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java @@ -136,6 +136,7 @@ public class SysOssController extends BaseController throw new FileSizeLimitExceededException(defaultMaxSize / 1024 / 1024); } SysOss oss = sysOssService.upload(file); + oss.setSize(file.getSize()); return R.ok(oss); } diff --git a/sf-file/src/main/java/com/sf/file/domain/SysOss.java b/sf-file/src/main/java/com/sf/file/domain/SysOss.java index c659941..a850fc5 100644 --- a/sf-file/src/main/java/com/sf/file/domain/SysOss.java +++ b/sf-file/src/main/java/com/sf/file/domain/SysOss.java @@ -47,6 +47,10 @@ public class SysOss extends BaseEntity @Excel(name = "服务商") private String service; + /** 文件大小 */ + @Excel(name = "文件大小") + private Long size; + public void setId(String id) { this.id = id; @@ -119,6 +123,13 @@ public class SysOss extends BaseEntity { return service; } + public Long getSize() { + return size; + } + + public void setSize(Long size) { + this.size = size; + } @Override public String toString() { @@ -135,6 +146,7 @@ public class SysOss extends BaseEntity .append("updateTime", getUpdateTime()) .append("updateBy", getUpdateBy()) .append("service", getService()) + .append("size",getSize()) .toString(); } } From 897f3d6ed0422d8caabe73751905537991d740e1 Mon Sep 17 00:00:00 2001 From: pengren Date: Tue, 16 Apr 2024 10:23:25 +0800 Subject: [PATCH 04/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E4=BF=AE=E6=94=B9oss=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf-admin/src/main/resources/application.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sf-admin/src/main/resources/application.yml b/sf-admin/src/main/resources/application.yml index 24adc4f..843f1c4 100644 --- a/sf-admin/src/main/resources/application.yml +++ b/sf-admin/src/main/resources/application.yml @@ -137,10 +137,10 @@ file: defaultMaxSize: 52428800 access: #key: iEJhVfZ8ColMeAnooVFx - key: UVtE4aBgbzCfYthfCV9P + key: CvproVoq7XiXDTK1RLFT secret: #key: SNTORcnq76xiaegoJ7Ap7Clldoq8HJUXqyn484o0 - key: 5rOuC0FPPrD2qEj2Eux4sGKKDgAKQ6dEatfLJOpZ + key: dekJkBHBkO4HccNKsiqAC2mkeNxVKEWSRoll3yqp bucket: #name: ruoyi - name: sc-device-manage + name: sac From bdd65363df519033c4ba5197c15bc66ff9c1bf73 Mon Sep 17 00:00:00 2001 From: pengren Date: Tue, 16 Apr 2024 09:45:04 +0800 Subject: [PATCH 05/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/sf/file/controller/SysOssController.java | 1 + sf-file/src/main/java/com/sf/file/domain/SysOss.java | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java index 55b91c5..379ec92 100644 --- a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java +++ b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java @@ -136,6 +136,7 @@ public class SysOssController extends BaseController throw new FileSizeLimitExceededException(defaultMaxSize / 1024 / 1024); } SysOss oss = sysOssService.upload(file); + oss.setSize(file.getSize()); return R.ok(oss); } diff --git a/sf-file/src/main/java/com/sf/file/domain/SysOss.java b/sf-file/src/main/java/com/sf/file/domain/SysOss.java index c659941..a850fc5 100644 --- a/sf-file/src/main/java/com/sf/file/domain/SysOss.java +++ b/sf-file/src/main/java/com/sf/file/domain/SysOss.java @@ -47,6 +47,10 @@ public class SysOss extends BaseEntity @Excel(name = "服务商") private String service; + /** 文件大小 */ + @Excel(name = "文件大小") + private Long size; + public void setId(String id) { this.id = id; @@ -119,6 +123,13 @@ public class SysOss extends BaseEntity { return service; } + public Long getSize() { + return size; + } + + public void setSize(Long size) { + this.size = size; + } @Override public String toString() { @@ -135,6 +146,7 @@ public class SysOss extends BaseEntity .append("updateTime", getUpdateTime()) .append("updateBy", getUpdateBy()) .append("service", getService()) + .append("size",getSize()) .toString(); } } From d20c6496891e2a871edddf8d0daaebb5d7d654b2 Mon Sep 17 00:00:00 2001 From: pengren Date: Tue, 16 Apr 2024 10:23:25 +0800 Subject: [PATCH 06/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E4=BF=AE=E6=94=B9oss=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf-admin/src/main/resources/application.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sf-admin/src/main/resources/application.yml b/sf-admin/src/main/resources/application.yml index 24adc4f..843f1c4 100644 --- a/sf-admin/src/main/resources/application.yml +++ b/sf-admin/src/main/resources/application.yml @@ -137,10 +137,10 @@ file: defaultMaxSize: 52428800 access: #key: iEJhVfZ8ColMeAnooVFx - key: UVtE4aBgbzCfYthfCV9P + key: CvproVoq7XiXDTK1RLFT secret: #key: SNTORcnq76xiaegoJ7Ap7Clldoq8HJUXqyn484o0 - key: 5rOuC0FPPrD2qEj2Eux4sGKKDgAKQ6dEatfLJOpZ + key: dekJkBHBkO4HccNKsiqAC2mkeNxVKEWSRoll3yqp bucket: #name: ruoyi - name: sc-device-manage + name: sac From d0b1a07bdbea7b57e0215643e6f2414f6a79fec6 Mon Sep 17 00:00:00 2001 From: pengren Date: Tue, 16 Apr 2024 16:51:22 +0800 Subject: [PATCH 07/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E6=96=B0=E5=A2=9E=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/sf/index/domain/ApplyListInfo.java | 132 ++++++++++-------- .../impl/ApplyListInfoServiceImpl.java | 2 + .../mapper/index/ApplyListInfoMapper.xml | 6 +- .../com/sf/common/utils/uuid/IdUtils.java | 35 +++-- 4 files changed, 103 insertions(+), 72 deletions(-) diff --git a/sf-admin/src/main/java/com/sf/index/domain/ApplyListInfo.java b/sf-admin/src/main/java/com/sf/index/domain/ApplyListInfo.java index 65eba05..47eaa62 100644 --- a/sf-admin/src/main/java/com/sf/index/domain/ApplyListInfo.java +++ b/sf-admin/src/main/java/com/sf/index/domain/ApplyListInfo.java @@ -7,127 +7,147 @@ import com.sf.common.core.domain.BaseEntity; /** * 应用列对象 APPLY_LIST_INFO - * + * * @author ztzh * @date 2024-04-11 */ -public class ApplyListInfo extends BaseEntity -{ +public class ApplyListInfo extends BaseEntity { private static final long serialVersionUID = 1L; - /** 主键 */ + /** + * 主键 + */ private Long id; - - /** 应用名称 */ + /** + * 应用code + */ + @Excel(name = "应用编号") + private String appCode; + /** + * 应用名称 + */ @Excel(name = "应用名称") private String appName; - /** 应用描述 */ + /** + * 应用描述 + */ @Excel(name = "应用描述") private String appDesc; - /** 图片 */ + /** + * 图片 + */ @Excel(name = "图片") private String picture; - /** 排序 */ + /** + * 排序 + */ private Long orderNum; - /** 逻辑删除,0:未删除,1:删除 */ + /** + * 逻辑删除,0:未删除,1:删除 + */ private Long isDelete; - /** 创建人 */ + /** + * 创建人 + */ private String created; - /** 更新人 */ + /** + * 更新人 + */ private String modified; - public void setId(Long id) - { + public void setId(Long id) { this.id = id; } - public Long getId() - { + public Long getId() { return id; } - public void setAppName(String appName) - { + + public void setAppName(String appName) { this.appName = appName; } - public String getAppName() - { + public String getAppName() { return appName; } - public void setAppDesc(String appDesc) - { + + public void setAppDesc(String appDesc) { this.appDesc = appDesc; } - public String getAppDesc() - { + public String getAppDesc() { return appDesc; } - public void setPicture(String picture) - { + + public void setPicture(String picture) { this.picture = picture; } - public String getPicture() - { + public String getPicture() { return picture; } - public void setOrderNum(Long orderNum) - { + + public void setOrderNum(Long orderNum) { this.orderNum = orderNum; } - public Long getOrderNum() - { + public Long getOrderNum() { return orderNum; } - public void setIsDelete(Long isDelete) - { + + public void setIsDelete(Long isDelete) { this.isDelete = isDelete; } - public Long getIsDelete() - { + public Long getIsDelete() { return isDelete; } - public void setCreated(String created) - { + + public void setCreated(String created) { this.created = created; } - public String getCreated() - { + public String getCreated() { return created; } - public void setModified(String modified) - { + + public void setModified(String modified) { this.modified = modified; } - public String getModified() - { + public String getModified() { return modified; } + public void setAppCode(String appCode) { + this.appCode = appCode; + } + + public String getAppCode() { + return appCode; + } + @Override public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("id", getId()) - .append("appName", getAppName()) - .append("appDesc", getAppDesc()) - .append("picture", getPicture()) - .append("orderNum", getOrderNum()) - .append("isDelete", getIsDelete()) - .append("created", getCreated()) - .append("modified", getModified()) - .append("createTime", getCreateTime()) - .append("updateTime", getUpdateTime()) - .toString(); + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("appCode",getAppCode()) + .append("appName", getAppName()) + .append("appDesc", getAppDesc()) + .append("picture", getPicture()) + .append("orderNum", getOrderNum()) + .append("isDelete", getIsDelete()) + .append("created", getCreated()) + .append("modified", getModified()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .toString(); } + } diff --git a/sf-admin/src/main/java/com/sf/index/service/impl/ApplyListInfoServiceImpl.java b/sf-admin/src/main/java/com/sf/index/service/impl/ApplyListInfoServiceImpl.java index 5181ce4..c77d3e8 100644 --- a/sf-admin/src/main/java/com/sf/index/service/impl/ApplyListInfoServiceImpl.java +++ b/sf-admin/src/main/java/com/sf/index/service/impl/ApplyListInfoServiceImpl.java @@ -2,6 +2,7 @@ package com.sf.index.service.impl; import java.util.List; import com.sf.common.utils.DateUtils; +import com.sf.common.utils.uuid.IdUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.sf.index.mapper.ApplyListInfoMapper; @@ -54,6 +55,7 @@ public class ApplyListInfoServiceImpl implements IApplyListInfoService public int insertApplyListInfo(ApplyListInfo applyListInfo) { applyListInfo.setCreateTime(DateUtils.getNowDate()); + applyListInfo.setAppCode(IdUtils.randomTime("ZT")); return applyListInfoMapper.insertApplyListInfo(applyListInfo); } diff --git a/sf-admin/src/main/resources/mapper/index/ApplyListInfoMapper.xml b/sf-admin/src/main/resources/mapper/index/ApplyListInfoMapper.xml index bcc6625..6ab3a1f 100644 --- a/sf-admin/src/main/resources/mapper/index/ApplyListInfoMapper.xml +++ b/sf-admin/src/main/resources/mapper/index/ApplyListInfoMapper.xml @@ -6,6 +6,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -18,7 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id, app_name, app_desc, picture, order_num, is_delete, created, modified, create_time, update_time from APPLY_LIST_INFO + select id, app_code, app_name, app_desc, picture, order_num, is_delete, created, modified, create_time, update_time from APPLY_LIST_INFO @@ -88,9 +91,14 @@ where id = #{id} + + - insert into ORDER_INFO + insert into Order_info id, order_no, @@ -146,7 +154,7 @@ - update ORDER_INFO + update Order_info order_no = #{orderNo}, order_status = #{orderStatus}, @@ -176,11 +184,11 @@ - delete from ORDER_INFO where id = #{id} + delete from Order_info where id = #{id} - delete from ORDER_INFO where id in + delete from Order_info where id in #{id} diff --git a/sf-payment/pom.xml b/sf-payment/pom.xml new file mode 100644 index 0000000..b8bc13d --- /dev/null +++ b/sf-payment/pom.xml @@ -0,0 +1,41 @@ + + + + smarterFramework + com.smarterFramework + 1.0.0 + + 4.0.0 + + sf-payment + + + 支付模块 + + + + + + + com.smarterFramework + sf-common + + + com.smarterFramework + sf-framework + + + com.smarterFramework + sf-order + + + org.bouncycastle + bcprov-jdk18on + 1.73 + + + + + \ No newline at end of file diff --git a/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java b/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java new file mode 100644 index 0000000..e52acb7 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java @@ -0,0 +1,31 @@ +package com.sf.payment.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + + +/** + * 功能描述: + * + * @author a_kun + * @date 2024/4/17 9:23 + */ +@Data +@Configuration +public class HuaweiPaymentConfig { + + /** + * 客户端id:对应各平台的appKey + */ + @Value("${huawei.payment.clientId:110693217}") + private String clientId; + + /** + * 客户端Secret:对应各平台的appSecret + */ + @Value("${huawei.payment.clientSecret:1410c01bc71c7ba587175ae79e500137c70945acc1416a38127cf98a09a6f8ba}") + private String clientSecret; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java b/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java new file mode 100644 index 0000000..baa770d --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java @@ -0,0 +1,46 @@ +package com.sf.payment.controller; + +import com.alibaba.fastjson2.JSONObject; +import com.sf.common.core.domain.AjaxResult; +import com.sf.payment.domain.HuaweiPaymentCallback; +import com.sf.payment.domain.HuaweiPurchasesVerifyDTO; +import com.sf.payment.service.IHuaweiPaymentService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author zoukun + */ +@Slf4j +@RestController +@RequestMapping("/payment") +public class HuaweiPaymentController { + + @Autowired + private IHuaweiPaymentService huaweiPaymentService; + + + /** + * 华为支付回调地址 + */ + @RequestMapping("/callback/huawei") + public AjaxResult callback(@RequestBody HuaweiPaymentCallback callback) { + log.info("进入callback: params:" + JSONObject.toJSONString(callback)); + AjaxResult ajax = AjaxResult.success(); + return ajax; + } + + + /** + * 华为购买验证 + */ + @RequestMapping("/huawei/purchases/verify") + public AjaxResult purchasesVerify(@Validated @RequestBody HuaweiPurchasesVerifyDTO verifyDTO) { + log.info("进入/huawei/purchases/tokens/verify: params:" + JSONObject.toJSONString(verifyDTO)); + huaweiPaymentService.purchasesVerify(verifyDTO); + return AjaxResult.success(); + } + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/AuthToken.java b/sf-payment/src/main/java/com/sf/payment/domain/AuthToken.java new file mode 100644 index 0000000..20bd856 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/AuthToken.java @@ -0,0 +1,42 @@ +package com.sf.payment.domain; + +import lombok.*; + +import java.io.Serializable; + +/** + * 授权所需的token + * + * @author zoukun + */ +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthToken implements Serializable { + private String accessToken; + private int expireIn; + private String refreshToken; + private int refreshTokenExpireIn; + private String uid; + private String openId; + private String accessCode; + private String unionId; + + /** + * 华为返回 生成的Access Token中包含的scope。 + */ + private String scope; + + /** + * 华为返回 固定返回Bearer,标识返回Access Token的类型 + */ + private String tokenType; + + /** + * 华为返回 返回JWT格式数据,包含用户基本帐号、用户邮箱等信息。 + * 参照https://developer.huawei.com/consumer/cn/doc/HMSCore-References/account-verify-id-token_hms_reference-0000001050050577#section3142132691914 + */ + private String idToken; +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/AuthUser.java b/sf-payment/src/main/java/com/sf/payment/domain/AuthUser.java new file mode 100644 index 0000000..03eaa4b --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/AuthUser.java @@ -0,0 +1,73 @@ +package com.sf.payment.domain; + +import com.alibaba.fastjson2.JSONObject; +import lombok.*; + +import java.io.Serializable; + +/** + * 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同 + * + * @author zoukun + */ +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthUser implements Serializable { + /** + * 用户第三方系统的唯一id。 + */ + private String uuid; + /** + * 用户名 + */ + private String username; + /** + * 用户昵称 + */ + private String nickname; + /** + * 用户头像 + */ + private String avatar; + /** + * 用户网址 + */ + private String blog; + /** + * 所在公司 + */ + private String company; + /** + * 位置 + */ + private String location; + /** + * 用户邮箱 + */ + private String email; + /** + * 用户手机号 + */ + private String mobileNumber; + /** + * 用户备注(各平台中的用户个人介绍) + */ + private String remark; + /** + * 用户来源 + */ + private String source; + /** + * 用户授权的token信息 + */ + private AuthToken token; + /** + * 第三方平台返回的原始用户信息 + */ + private JSONObject rawUserInfo; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPaymentCallback.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPaymentCallback.java new file mode 100644 index 0000000..b7e46d2 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPaymentCallback.java @@ -0,0 +1,35 @@ +package com.sf.payment.domain; + +import lombok.*; + +import java.io.Serializable; + +/** + * 授权回调时的参数类 + * + * @author zk + */ +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class HuaweiPaymentCallback implements Serializable { + + /** + * 访问AuthorizeUrl后回调时带的参数code + */ + private String code; + + /** + * 客户端id:对应各平台的appKey + */ + private String clientId; + + /** + * 客户端Secret:对应各平台的appSecret + */ + private String clientSecret; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java new file mode 100644 index 0000000..563f879 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java @@ -0,0 +1,51 @@ +package com.sf.payment.domain; + +import apijson.NotNull; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * 华为验证购买tokenDTO + * + * @author zk + */ +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class HuaweiPurchasesVerifyDTO implements Serializable { + + + + /** + * 商品类别,取值包括: + * + * 0:消耗型商品 + * 1:非消耗型商品 + * 2:订阅型商品 + */ + @NotNull + private Integer kind; + + /** + * 待下发商品的购买Token,发起购买和查询待消费商品信息时均会返回purchaseToken参数。 + */ + @NotBlank(message = "待下发商品的购买Token不能为空") + private String purchaseToken; + + /** + * 待下发商品ID。商品ID来源于您在AppGallery Connect中配置商品信息时设置的商品ID。 + */ + @NotBlank(message = "待下发商品ID不能为空") + private String productId; + + /** + * 订单号 + */ + @NotBlank(message = "订单号不能为空") + private String orderNo; + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyResponseDTO.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyResponseDTO.java new file mode 100644 index 0000000..c684a59 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyResponseDTO.java @@ -0,0 +1,56 @@ +package com.sf.payment.domain; + +import apijson.NotNull; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * 华为验证购买tokenD返回 + * + * @author zk + */ +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class HuaweiPurchasesVerifyResponseDTO implements Serializable { + + /** + * 返回码。 + * + * 0:成功。 + * 其他:失败,具体请参见错误码。 + */ + private String responseCode; + + /** + * 响应描述。 + */ + private String responseMessage; + + /** + * + * 包含购买数据的JSON字符串,具体请参见表InappPurchaseDetails。 + * + * 该字段原样参与签名。 + */ + private String purchaseTokenData; + + /** + * purchaseTokenData基于应用RSA IAP私钥的签名信息,签名算法为signatureAlgorithm。 + * 应用请参见对返回结果验签使用IAP公钥对PurchaseTokenData的JSON字符串进行验签。 + */ + private String dataSignature; + + /** + * + * 签名算法。 + */ + @NotBlank(message = "待下发商品ID不能为空") + private String signatureAlgorithm; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/InAppPurchaseData.java b/sf-payment/src/main/java/com/sf/payment/domain/InAppPurchaseData.java new file mode 100644 index 0000000..05f7e97 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/InAppPurchaseData.java @@ -0,0 +1,233 @@ +package com.sf.payment.domain; + +import lombok.Data; + +/** + * 功能描述: + * + * @author a_kun + * @date 2024/4/16 15:29 + */ +@Data +public class InAppPurchaseData { + + /** + * 应用ID。 + */ + private Long applicationId; + + /** + * 消耗型商品或者非消耗型商品:固定为false。 + * + * 订阅型商品: + * + * true:订阅处于活动状态并且将在下一个结算日期自动续订。 + * false:用户已经取消订阅。 用户可以在下一个结算日期之前访问订阅内容,并且在该日期后将无法访问,除非重新启用自动续订。 如果提供了宽限期,只要宽限期未过,此值就会对所有订阅保持设置为true。 下一次结算日期每天都会自动延长,直至宽限期结束,或者用户更改付款方式。 + */ + private Boolean autoRenewing; + + /** + * 订单ID,唯一标识一笔需要收费的收据,由华为应用内支付服务器在创建订单以及订阅型商品续费时生成。 + * + * 每一笔新的收据都会使用不同的orderId。 + */ + private String orderId; + + /** + * 商品类别,取值包括: + * + * 0:消耗型商品 + * 1:非消耗型商品 + * 2:订阅型商品 + */ + private Integer kind; + + /** + * 商品ID。每种商品必须有唯一的ID,由应用在PMS中维护,或者应用发起购买时传入。 + * 说明 + * 为避免资金损失,您在对支付结果验签成功后,必须对其进行校验。 + */ + private String productId; + + /** + * 商品名称。 + */ + private String productName; + + /** + * 商品购买时间,UTC时间戳,以毫秒为单位。 + */ + private Long purchaseTime; + + /** + * 订单交易状态。 + * + * -1:初始化 + * 0:已购买 + * 1:已取消 + * 2:已撤销或已退款 + * 3:待处理 + */ + private Integer purchaseState; + + /** + * + * 商户侧保留信息,由您在调用支付接口时传入。 + */ + private String developerPayload; + + /** + *消耗状态,仅一次性商品存在,取值包括: + * + * 0:未消耗 + * 1:已消耗 + */ + private Integer consumptionState; + + /** + *确认状态,取值包括: + * + * 0 :未确认 + * 1:已确认 + * 没有值表示不需要确认 + */ + private Integer confirmed; + + /** + * 用于唯一标识商品和用户对应关系的购买令牌,在支付完成时由华为应用内支付服务器生成。 + * 说明 + * 该字段是唯一标识商品和用户对应关系的,在订阅型商品正常续订时不会改变。 + * 当前92位,后续存在扩展可能,如要进行存储,建议您预留128位的长度。 + * 如要进行存储,为保证安全,建议加密存储。 + */ + private String purchaseToken; + + /** + * 用定价货币的币种,请参见ISO 4217标准。 + * 说明 + * 为避免资金损失,您在对支付结果验签成功后,必须对其进行校验。 + */ + private String currency; + + /** + * 商品实际价格*100以后的值。商品实际价格精确到小数点后2位,例如此参数值为501,则表示商品实际价格为5.01。 + */ + private Long price; + + /** + * 支付方式,取值请参见payType说明。 + */ + private String payType; + + /** + * 交易单号,用户支付成功后生成。 + */ + private String payOrderId; + + // 以下参数只在订阅场景返回 + + + /** + * 上次续期收款的订单ID,由支付服务器在续期扣费时生成。首次购买订阅型商品时的lastOrderId与orderId数值相同。 + */ + private String lastOrderId; + + /** + * 订阅型商品所属的订阅组ID。 + */ + private String productGroup; + + /** + * 原购买的时间,即本订阅型商品首次成功收费的时间,UTC时间戳,以毫秒为单位。。 + */ + private Long oriPurchaseTime; + + /** + * 订阅ID。 + * 说明 + * subscriptionId是用户与商品之间的一一对应关系,在订阅型商品正常续订时不会改变。 + */ + private String subscriptionId; + + /** + * 原订阅ID。有值表示当前订阅是从其他商品切换来的,该值可以关联切换前的商品订阅信息 + */ + private String oriSubscriptionId; + + /** + * 购买数量。 + */ + private Integer quantity; + + /** + * 已经付费订阅的天数,免费试用和促销期周期除外。。 + */ + private Long daysLasted; + + /** + * 成功标准续期(没有设置促销的续期)的期数,为0或者不存在表示还没有成功续期。 + */ + private Long numOfPeriods; + + /** + * 成功促销续期期数。 + */ + private Long numOfDiscount; + + /** + * 订阅型商品过期时间,UTC时间戳,以毫秒为单位。 + * + * 对于一个成功收费的自动续订收据,该时间表示续期日期或者超期日期。如果商品最近的收据的该时间是一个过去的时间,则订阅已经过期。 + */ + private Long expirationDate; + + /** + * 对于已经过期的订阅,表示过期原因,取值包括: + * + * 1:用户取消 + * 2:商品不可用 + * 3:用户签约信息异常 + * 4:Billing错误 + * 5:用户未同意涨价 + * 6:未知错误 + * 同时有多个异常时,优先级为:1 > 2 > 3… + */ + private Integer expirationIntent; + + /** + * 订阅撤销时间,UTC时间戳,以毫秒为单位,发生退款且服务立即不可用。在顾客投诉,通过客服撤销订阅,或者顾客升级、跨级到同组其他商品并且立即生效场景下,需要撤销原有订阅的上次收据时有值。 + * 说明 + * 已经撤销的收据等同于没有完成购买。 + */ + private Long cancelTime; + + /** + * 取消原因。 + * + * 3: 应用调用IAP接口取消 + * 2:顾客升级、跨级等。 + * 1:顾客因为在App内遇到了问题而取消了订阅。 + * 0:其他原因取消,比如顾客错误地订阅了商品。 + * 说明 + * 如果为空且cancelTime有值,表示是升级等操作导致的取消。 + */ + private Integer cancelReason; + + /** + * 续期状态。 + * + * 1:当前周期到期时自动续期 + * 0:用户停止了续期 + * 仅针对自动续期订阅,对有效和过期的订阅均有效,并不代表顾客的订阅状态。通常,取值为0时,应用可以给顾客提供其他的订阅选项, + * 例如推荐一个同组更低级别的商品。该值为0通常代表着顾客主动取消了该订阅。 + */ + private Integer renewStatus; + + /** + * 用户取消订阅的时间,UTC时间戳,以毫秒为单位。在该时间进行了订阅续期停止的设定,商品在有效期内仍然有效,但后续的续期会终止,无退款。 + */ + private Integer cancellationTime; + + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/service/IHuaweiPaymentService.java b/sf-payment/src/main/java/com/sf/payment/service/IHuaweiPaymentService.java new file mode 100644 index 0000000..1383be3 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/service/IHuaweiPaymentService.java @@ -0,0 +1,14 @@ +package com.sf.payment.service; + + +import com.sf.payment.domain.HuaweiPurchasesVerifyDTO; + +/** + * 功能描述: + * + * @author a_kun + * @date 2024/4/12 10:21 + */ +public interface IHuaweiPaymentService { + void purchasesVerify(HuaweiPurchasesVerifyDTO verifyDTO); +} diff --git a/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java b/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java new file mode 100644 index 0000000..2cf3347 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java @@ -0,0 +1,235 @@ +package com.sf.payment.service.impl; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.sf.common.utils.SecurityUtils; +import com.sf.order.domain.OrderInfo; +import com.sf.order.service.IOrderInfoService; +import com.sf.payment.config.HuaweiPaymentConfig; +import com.sf.payment.domain.HuaweiPurchasesVerifyDTO; +import com.sf.payment.domain.HuaweiPurchasesVerifyResponseDTO; +import com.sf.payment.domain.InAppPurchaseData; +import com.sf.payment.service.IHuaweiPaymentService; +import com.sf.system.domain.UserMember; +import com.sf.system.service.IUserMemberService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.Security; +import java.security.spec.X509EncodedKeySpec; +import java.text.MessageFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + + +/** + * 功能描述: + * + * @author a_kun + * @date 2024/4/12 10:21 + */ +@Slf4j +@Service +public class HuaweiPaymentServiceImpl implements IHuaweiPaymentService { + + // token url to get the authorization + private static final String TOKEN_URL = "https://oauth-login.cloud.huawei.com/oauth2/v3/token"; + private static final String VERIFY_TOKEN_URL = "https://orders-drcn.iap.cloud.huawei.com.cn/applications/purchases/tokens/verify"; + + private static final String PUBLIC_KEY = "PUBLIC_KEY"; + + @Autowired + private IOrderInfoService orderInfoService; + + @Autowired + private IUserMemberService userMemberService; + + @Autowired + private HuaweiPaymentConfig huaweiPaymentConfig; + + @Override + @Transactional(rollbackFor = Exception.class) + public void purchasesVerify(HuaweiPurchasesVerifyDTO verifyDTO) { + // construct the Authorization in Header + Map headers = buildAuthorization(getAppAT(huaweiPaymentConfig.getClientId(), huaweiPaymentConfig.getClientSecret())); + + // pack the request body + Map bodyMap = new HashMap<>(); + bodyMap.put("purchaseToken", verifyDTO.getPurchaseToken()); + bodyMap.put("productId", verifyDTO.getProductId()); + + String response = HttpUtil.createPost(VERIFY_TOKEN_URL) + .addHeaders(headers) + .body(JSON.toJSONString(bodyMap)) + .execute().body(); + HuaweiPurchasesVerifyResponseDTO huaweiPurchasesVerifyResponseDTO = JSON.parseObject(response, HuaweiPurchasesVerifyResponseDTO.class); + InAppPurchaseData inAppPurchaseData = JSON.parseObject(huaweiPurchasesVerifyResponseDTO.getPurchaseTokenData(), InAppPurchaseData.class); + // 获取服务订单 + OrderInfo orderInfo = orderInfoService.selectOrderInfoByOrderNo(verifyDTO.getOrderNo()); + // 校验订单 + boolean checkSuccessOrder = checkSuccessOrder(huaweiPurchasesVerifyResponseDTO.getPurchaseTokenData() + , huaweiPurchasesVerifyResponseDTO.getDataSignature() + , PUBLIC_KEY + , huaweiPurchasesVerifyResponseDTO.getSignatureAlgorithm() + , orderInfo); + + Assert.isTrue(checkSuccessOrder, "订单校验失败,请重试"); + Assert.isTrue(inAppPurchaseData.getPurchaseState()==0, "订单未完成购买"); + DateTime payTime = DateUtil.date(inAppPurchaseData.getPurchaseTime()); + UserMember userMember = userMemberService.selectUserMemberByUserId(orderInfo.getCreateUserId()); + if (userMember == null) { + // 添加会员信息 + boolean isSubscription = verifyDTO.getKind() == 2; + userMember = new UserMember(); + userMember.setMemberLevel(isSubscription ? 1 : 2); + userMember.setSubscriptionStatus(isSubscription ? 1 : 0); + userMember.setUserId(orderInfo.getCreateUserId()); + userMember.setIntegration(0L); + DateTime expirationTime = DateUtil.offset(payTime, DateField.MONTH, 1); + userMember.setExpirationTime(expirationTime); + userMember.setCreateTime(new Date()); + userMember.setUpdateTime(new Date()); + userMemberService.insertUserMember(userMember); + } else { + // 更新 + DateTime expirationTime = DateUtil.offset(userMember.getExpirationTime(), DateField.MONTH, 1); + userMember.setExpirationTime(expirationTime); + userMember.setUpdateTime(new Date()); + userMemberService.updateUserMember(userMember); + } + // 更新订单状态 + orderInfo.setPayTime(payTime); + orderInfo.setOrderStatus(3L); + orderInfo.setPayChannel(2L); + orderInfo.setPayAmt(orderInfo.getOrderAmt()); + orderInfo.setReallyAmt(orderInfo.getOrderAmt()); + orderInfo.setOrderStatus(3L); + orderInfo.setPayData(huaweiPurchasesVerifyResponseDTO.getPurchaseTokenData()); + orderInfoService.updateOrderInfo(orderInfo); + } + + /** + * Gets App Level AccessToken. + */ + public static String getAppAT(String clientId, String clientSecret) { + // fetch accessToken + Map form = new HashMap<>(8); + form.put("grant_type", "client_credentials"); + form.put("client_secret", clientSecret); + form.put("client_id", clientId); + String atResponse = HttpUtil.post(TOKEN_URL, form); + log.info("getAppAT Response : {}", atResponse); + JSONObject parseObject = JSON.parseObject(atResponse); + return parseObject.getString("access_token"); + } + + /** + * Build Authorization in Header + * + * @param appAt appAt + * @return headers + */ + public static Map buildAuthorization(String appAt) { + String oriString = MessageFormat.format("APPAT:{0}", appAt); + String authorization = + MessageFormat.format("Basic {0}", Base64.encodeBase64String(oriString.getBytes(StandardCharsets.UTF_8))); + Map headers = new HashMap<>(); + headers.put("Authorization", authorization); + headers.put("Content-Type", "application/json; charset=UTF-8"); + return headers; + } + + /** + * 校验签名信息,校验InAppPurchaseData中的productId、price、currency等信息的一致性 + * + * @param content 结果字符串 + * @param sign 签名字符串 + * @param publicKey IAP公钥 + * @return 是否校验通过 + */ + public static boolean checkSuccessOrder(String content, String sign, String publicKey, String signatureAlgorithm, OrderInfo orderInfo) { + // 校验签名信息 + boolean checkRes = checkSign(content, sign, publicKey, StrUtil.blankToDefault(signatureAlgorithm, "SHA256WithRSA")); + if (checkRes) { + // 校验InAppPurchaseData中的productId、price、currency等信息的一致性 + checkRes = checkProductIdAndPriceAndCurrency(content, orderInfo); + } + return checkRes; + } + + /** + * 校验InAppPurchaseData中的productId、price、currency等信息的一致性 + * + * @param content 结果字符串 + * @param yourOrderInfo 您的订单信息,包括productId、price、currency + * @return 是否校验通过 + */ + public static boolean checkProductIdAndPriceAndCurrency(String content, OrderInfo yourOrderInfo) { + InAppPurchaseData inAppPurchaseData = JSON.parseObject(content, InAppPurchaseData.class); + // 校验InAppPurchaseData中的productId、price、currency等信息的一致性 + return inAppPurchaseData.getProductId().equals(yourOrderInfo.getGoodsId()) + && inAppPurchaseData.getPrice().equals(yourOrderInfo.getOrderAmt()); + } + + /** + * 校验签名信息 + * + * @param content 结果字符串 + * @param sign 签名字符串 + * @param publicKey IAP公钥 + * @param signatureAlgorithm 签名算法字段,可从接口返回数据中获取,例如:OwnedPurchasesResult.getSignatureAlgorithm() + * @return 是否校验通过 + */ + public static boolean checkSign(String content, String sign, String publicKey, String signatureAlgorithm) { + if (sign == null) { + return false; + } + if (publicKey == null) { + return false; + } + + // 当signatureAlgorithm为空时使用默认签名算法 + if (signatureAlgorithm == null || signatureAlgorithm.length() == 0) { + signatureAlgorithm = "SHA256WithRSA"; + System.out.println("doCheck, algorithm: SHA256WithRSA"); + } + try { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + // 生成"RSA"的KeyFactory对象 + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + byte[] decodedKey = Base64.decodeBase64(publicKey); + // 生成公钥 + PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey)); + java.security.Signature signature = null; + // 根据SHA256WithRSA算法获取签名对象实例 + signature = java.security.Signature.getInstance(signatureAlgorithm); + // 初始化验证签名的公钥 + signature.initVerify(pubKey); + // 把原始报文更新到签名对象中 + signature.update(content.getBytes(StandardCharsets.UTF_8)); + // 将sign解码 + byte[] bsign = Base64.decodeBase64(sign); + // 进行验签 + return signature.verify(bsign); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + +} diff --git a/sf-system/src/main/java/com/sf/system/domain/UserMember.java b/sf-system/src/main/java/com/sf/system/domain/UserMember.java new file mode 100644 index 0000000..e298567 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/domain/UserMember.java @@ -0,0 +1,110 @@ +package com.sf.system.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.sf.common.annotation.Excel; +import com.sf.common.core.domain.BaseEntity; + +/** + * 会员对象 User_member + * + * @author ztzh + * @date 2024-04-16 + */ +public class UserMember extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** $column.columnComment */ + private Long id; + + /** 用户会员等级(0普通会员 1订阅会员 2月度会员) */ + @Excel(name = "用户会员等级", readConverterExp = "0=普通会员,1=订阅会员,2=月度会员") + private Integer memberLevel; + + /** 会员等级为订阅会员时的订阅状态,未订阅就是取消了(0未订阅 1已订阅) */ + @Excel(name = "会员等级为订阅会员时的订阅状态,未订阅就是取消了", readConverterExp = "0=未订阅,1=已订阅") + private Integer subscriptionStatus; + + /** 关联的用户id */ + @Excel(name = "关联的用户id") + private Long userId; + + /** 积分 */ + @Excel(name = "积分") + private Long integration; + + /** 过期时间 */ + private Date expirationTime; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setMemberLevel(Integer memberLevel) + { + this.memberLevel = memberLevel; + } + + public Integer getMemberLevel() + { + return memberLevel; + } + public void setSubscriptionStatus(Integer subscriptionStatus) + { + this.subscriptionStatus = subscriptionStatus; + } + + public Integer getSubscriptionStatus() + { + return subscriptionStatus; + } + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + public void setIntegration(Long integration) + { + this.integration = integration; + } + + public Long getIntegration() + { + return integration; + } + public void setExpirationTime(Date expirationTime) + { + this.expirationTime = expirationTime; + } + + public Date getExpirationTime() + { + return expirationTime; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("memberLevel", getMemberLevel()) + .append("subscriptionStatus", getSubscriptionStatus()) + .append("userId", getUserId()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("integration", getIntegration()) + .append("expirationTime", getExpirationTime()) + .toString(); + } +} diff --git a/sf-system/src/main/java/com/sf/system/mapper/UserMemberMapper.java b/sf-system/src/main/java/com/sf/system/mapper/UserMemberMapper.java new file mode 100644 index 0000000..f5ad663 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/mapper/UserMemberMapper.java @@ -0,0 +1,63 @@ +package com.sf.system.mapper; + +import java.util.List; +import com.sf.system.domain.UserMember; + +/** + * 会员Mapper接口 + * + * @author ztzh + * @date 2024-04-16 + */ +public interface UserMemberMapper +{ + /** + * 查询会员 + * + * @param id 会员主键 + * @return 会员 + */ + public UserMember selectUserMemberById(Long id); + + /** + * 查询会员列表 + * + * @param userMember 会员 + * @return 会员集合 + */ + public List selectUserMemberList(UserMember userMember); + + /** + * 新增会员 + * + * @param userMember 会员 + * @return 结果 + */ + public int insertUserMember(UserMember userMember); + + /** + * 修改会员 + * + * @param userMember 会员 + * @return 结果 + */ + public int updateUserMember(UserMember userMember); + + /** + * 删除会员 + * + * @param id 会员主键 + * @return 结果 + */ + public int deleteUserMemberById(Long id); + + /** + * 批量删除会员 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteUserMemberByIds(Long[] ids); + + UserMember selectUserMemberByUserId(Long userId); +} diff --git a/sf-system/src/main/java/com/sf/system/service/IUserMemberService.java b/sf-system/src/main/java/com/sf/system/service/IUserMemberService.java new file mode 100644 index 0000000..8e40f1a --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/service/IUserMemberService.java @@ -0,0 +1,63 @@ +package com.sf.system.service; + +import java.util.List; +import com.sf.system.domain.UserMember; + +/** + * 会员Service接口 + * + * @author ztzh + * @date 2024-04-16 + */ +public interface IUserMemberService +{ + /** + * 查询会员 + * + * @param id 会员主键 + * @return 会员 + */ + public UserMember selectUserMemberById(Long id); + + /** + * 查询会员列表 + * + * @param userMember 会员 + * @return 会员集合 + */ + public List selectUserMemberList(UserMember userMember); + + /** + * 新增会员 + * + * @param userMember 会员 + * @return 结果 + */ + public int insertUserMember(UserMember userMember); + + /** + * 修改会员 + * + * @param userMember 会员 + * @return 结果 + */ + public int updateUserMember(UserMember userMember); + + /** + * 批量删除会员 + * + * @param ids 需要删除的会员主键集合 + * @return 结果 + */ + public int deleteUserMemberByIds(Long[] ids); + + /** + * 删除会员信息 + * + * @param id 会员主键 + * @return 结果 + */ + public int deleteUserMemberById(Long id); + + UserMember selectUserMemberByUserId(Long userId); +} diff --git a/sf-system/src/main/java/com/sf/system/service/impl/UserMemberServiceImpl.java b/sf-system/src/main/java/com/sf/system/service/impl/UserMemberServiceImpl.java new file mode 100644 index 0000000..6c2890c --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/service/impl/UserMemberServiceImpl.java @@ -0,0 +1,101 @@ +package com.sf.system.service.impl; + +import java.util.List; +import com.sf.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.sf.system.mapper.UserMemberMapper; +import com.sf.system.domain.UserMember; +import com.sf.system.service.IUserMemberService; + +/** + * 会员Service业务层处理 + * + * @author ztzh + * @date 2024-04-16 + */ +@Service +public class UserMemberServiceImpl implements IUserMemberService +{ + @Autowired + private UserMemberMapper userMemberMapper; + + /** + * 查询会员 + * + * @param id 会员主键 + * @return 会员 + */ + @Override + public UserMember selectUserMemberById(Long id) + { + return userMemberMapper.selectUserMemberById(id); + } + + /** + * 查询会员列表 + * + * @param userMember 会员 + * @return 会员 + */ + @Override + public List selectUserMemberList(UserMember userMember) + { + return userMemberMapper.selectUserMemberList(userMember); + } + + /** + * 新增会员 + * + * @param userMember 会员 + * @return 结果 + */ + @Override + public int insertUserMember(UserMember userMember) + { + userMember.setCreateTime(DateUtils.getNowDate()); + return userMemberMapper.insertUserMember(userMember); + } + + /** + * 修改会员 + * + * @param userMember 会员 + * @return 结果 + */ + @Override + public int updateUserMember(UserMember userMember) + { + userMember.setUpdateTime(DateUtils.getNowDate()); + return userMemberMapper.updateUserMember(userMember); + } + + /** + * 批量删除会员 + * + * @param ids 需要删除的会员主键 + * @return 结果 + */ + @Override + public int deleteUserMemberByIds(Long[] ids) + { + return userMemberMapper.deleteUserMemberByIds(ids); + } + + /** + * 删除会员信息 + * + * @param id 会员主键 + * @return 结果 + */ + @Override + public int deleteUserMemberById(Long id) + { + return userMemberMapper.deleteUserMemberById(id); + } + + @Override + public UserMember selectUserMemberByUserId(Long userId) { + return userMemberMapper.selectUserMemberByUserId(userId); + } +} diff --git a/sf-system/src/main/resources/mapper/system/UserMemberMapper.xml b/sf-system/src/main/resources/mapper/system/UserMemberMapper.xml new file mode 100644 index 0000000..3c8a8a0 --- /dev/null +++ b/sf-system/src/main/resources/mapper/system/UserMemberMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + select id, member_level, subscription_status, user_id, create_time, update_time, integration, expiration_time from User_member + + + + + + + + + + insert into User_member + + member_level, + subscription_status, + user_id, + create_time, + update_time, + integration, + expiration_time, + + + #{memberLevel}, + #{subscriptionStatus}, + #{userId}, + #{createTime}, + #{updateTime}, + #{integration}, + #{expirationTime}, + + + + + update User_member + + member_level = #{memberLevel}, + subscription_status = #{subscriptionStatus}, + user_id = #{userId}, + create_time = #{createTime}, + update_time = #{updateTime}, + integration = #{integration}, + expiration_time = #{expirationTime}, + + where id = #{id} + + + + delete from User_member where id = #{id} + + + + delete from User_member where id in + + #{id} + + + \ No newline at end of file From 9400c9985cadaa24bfe9e6ebf3b657bbd803913c Mon Sep 17 00:00:00 2001 From: akun <957746831@qq.com> Date: Thu, 18 Apr 2024 15:52:07 +0800 Subject: [PATCH 09/16] =?UTF-8?q?=E5=8D=8E=E4=B8=BA=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E5=86=85=E6=94=AF=E4=BB=98=E5=AE=8C=E6=88=90=E4=BC=9A=E5=91=98?= =?UTF-8?q?=E8=AE=A2=E8=B4=AD=EF=BC=8C=E4=BC=9A=E5=91=98=E4=B8=8E=E5=95=86?= =?UTF-8?q?=E5=93=81=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 11 + sf-admin/pom.xml | 8 + sf-admin/src/main/resources/application.yml | 2 +- .../order/controller/OrderInfoController.java | 2 - .../java/com/sf/order/domain/OrderInfo.java | 224 +---- .../sf/order/domain/res/OrderListResVo.java | 18 + .../sf/order/service/IOrderInfoService.java | 3 + .../service/impl/OrderInfoServiceImpl.java | 7 +- .../mapper/order/OrderInfoMapper.xml | 13 +- sf-payment/pom.xml | 9 + .../payment/config/HuaweiPaymentConfig.java | 8 +- .../sf/payment/constant/GoodsConstants.java | 29 + .../controller/HuaweiPaymentController.java | 4 +- .../java/com/sf/payment/domain/AuthToken.java | 42 - .../java/com/sf/payment/domain/AuthUser.java | 73 -- .../domain/HuaweiPurchasesVerifyDTO.java | 36 +- .../payment/domain/HuaweiQueryResponse.java | 35 + .../payment/domain/PurchaseOrderPayload.java | 165 ++++ .../payment/domain/SubGroupStatusPayload.java | 48 ++ .../com/sf/payment/domain/SubRenewalInfo.java | 91 +++ .../sf/payment/domain/SubscriptionStatus.java | 68 ++ .../impl/HuaweiPaymentServiceImpl.java | 288 +++++-- .../payment/utils/HuaweiTokenGenerator.java | 43 + ...ey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8 | 6 + sf-service/pom.xml | 33 + .../controller/GoodsMessagesController.java | 29 +- .../com/sf/service}/domain/GoodsMessages.java | 71 +- .../service}/mapper/GoodsMessagesMapper.java | 10 +- .../service/IGoodsMessagesService.java | 10 +- .../impl/GoodsMessagesServiceImpl.java | 21 +- .../mapper/service}/GoodsMessagesMapper.xml | 246 +++--- sf-ui/src/api/{goods => service}/goods.js | 88 +- .../views/{goods => service}/goods/index.vue | 771 ++++++++++-------- 33 files changed, 1548 insertions(+), 964 deletions(-) create mode 100644 sf-payment/src/main/java/com/sf/payment/constant/GoodsConstants.java delete mode 100644 sf-payment/src/main/java/com/sf/payment/domain/AuthToken.java delete mode 100644 sf-payment/src/main/java/com/sf/payment/domain/AuthUser.java create mode 100644 sf-payment/src/main/java/com/sf/payment/domain/HuaweiQueryResponse.java create mode 100644 sf-payment/src/main/java/com/sf/payment/domain/PurchaseOrderPayload.java create mode 100644 sf-payment/src/main/java/com/sf/payment/domain/SubGroupStatusPayload.java create mode 100644 sf-payment/src/main/java/com/sf/payment/domain/SubRenewalInfo.java create mode 100644 sf-payment/src/main/java/com/sf/payment/domain/SubscriptionStatus.java create mode 100644 sf-payment/src/main/java/com/sf/payment/utils/HuaweiTokenGenerator.java create mode 100644 sf-payment/src/main/resources/IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8 create mode 100644 sf-service/pom.xml rename {sf-admin/src/main/java/com/sf/goods => sf-service/src/main/java/com/sf/service}/controller/GoodsMessagesController.java (83%) rename {sf-admin/src/main/java/com/sf/goods => sf-service/src/main/java/com/sf/service}/domain/GoodsMessages.java (79%) rename {sf-admin/src/main/java/com/sf/goods => sf-service/src/main/java/com/sf/service}/mapper/GoodsMessagesMapper.java (87%) rename {sf-admin/src/main/java/com/sf/goods => sf-service/src/main/java/com/sf/service}/service/IGoodsMessagesService.java (87%) rename {sf-admin/src/main/java/com/sf/goods => sf-service/src/main/java/com/sf/service}/service/impl/GoodsMessagesServiceImpl.java (80%) rename {sf-admin/src/main/resources/mapper/goods => sf-service/src/main/resources/mapper/service}/GoodsMessagesMapper.xml (76%) rename sf-ui/src/api/{goods => service}/goods.js (76%) rename sf-ui/src/views/{goods => service}/goods/index.vue (67%) diff --git a/pom.xml b/pom.xml index a82b5d6..9c6a7a9 100644 --- a/pom.xml +++ b/pom.xml @@ -174,6 +174,17 @@ ${sf.version} + + com.smarterFramework + sf-payment + ${sf.version} + + + + com.smarterFramework + sf-service + ${sf.version} + com.smarterFramework diff --git a/sf-admin/pom.xml b/sf-admin/pom.xml index 49f0926..6a68f81 100644 --- a/sf-admin/pom.xml +++ b/sf-admin/pom.xml @@ -71,7 +71,15 @@ sf-order + + com.smarterFramework + sf-payment + + + com.smarterFramework + sf-service + diff --git a/sf-admin/src/main/resources/application.yml b/sf-admin/src/main/resources/application.yml index 843f1c4..d953204 100644 --- a/sf-admin/src/main/resources/application.yml +++ b/sf-admin/src/main/resources/application.yml @@ -18,7 +18,7 @@ sf: # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 - port: 7781 + port: 80 servlet: # 应用的访问路径 context-path: / diff --git a/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java b/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java index 21f3db2..2011062 100644 --- a/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java +++ b/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java @@ -3,10 +3,8 @@ package com.sf.order.controller; import com.sf.common.annotation.Log; import com.sf.common.core.controller.BaseController; import com.sf.common.core.domain.AjaxResult; -import com.sf.common.core.domain.entity.SysUser; import com.sf.common.core.page.TableDataInfo; import com.sf.common.enums.BusinessType; -import com.sf.common.utils.SecurityUtils; import com.sf.common.utils.poi.ExcelUtil; import com.sf.order.domain.OrderInfo; import com.sf.order.domain.dto.OrderCreateDto; diff --git a/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java b/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java index 6785f83..889350d 100644 --- a/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java +++ b/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java @@ -3,6 +3,7 @@ package com.sf.order.domain; import com.fasterxml.jackson.annotation.JsonFormat; import com.sf.common.annotation.Excel; import com.sf.common.core.domain.BaseEntity; +import lombok.Data; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -14,6 +15,7 @@ import java.util.Date; * @author ztzh * @date 2024-04-09 */ +@Data public class OrderInfo extends BaseEntity { private static final long serialVersionUID = 1L; @@ -26,7 +28,7 @@ public class OrderInfo extends BaseEntity { * 订单编号 */ @Excel(name = "订单编号") - private String orderNo; + private Long orderNo; /** * 订单状态: 0:待支付 1:已付款待发货 2:配送中 3:待取货 4:支付超时系统结束 5:客户自主取消 6:已完成 @@ -155,209 +157,27 @@ public class OrderInfo extends BaseEntity { @Excel(name = "减免金额(优惠券抵扣)") private Long reductionAmout; - public void setId(Long id) { - this.id = id; - } + /** + * * 商品数量 + */ + private Integer goodsCount; + /** + * * 商品类型。 + * * • 0:消耗型商品 + * * • 1:非消耗型商品 + * * • 2:自动续期订阅商品 + */ + private Integer goodsType; - public Long getId() { - return id; - } + /** + * 商品单价 + */ + private Long goodsPrice; - public void setOrderNo(String orderNo) { - this.orderNo = orderNo; - } + /** + * 商品编号 + */ + private String goodsCode; - public String getOrderNo() { - return orderNo; - } - public void setOrderStatus(Long orderStatus) { - this.orderStatus = orderStatus; - } - - public Long getOrderStatus() { - return orderStatus; - } - - public void setPayType(Long payType) { - this.payType = payType; - } - - public Long getPayType() { - return payType; - } - - public void setPayChannel(Long payChannel) { - this.payChannel = payChannel; - } - - public Long getPayChannel() { - return payChannel; - } - - public void setOrderAmt(Long orderAmt) { - this.orderAmt = orderAmt; - } - - public Long getOrderAmt() { - return orderAmt; - } - - public void setFreightAmt(Long freightAmt) { - this.freightAmt = freightAmt; - } - - public Long getFreightAmt() { - return freightAmt; - } - - public void setPayAmt(Long payAmt) { - this.payAmt = payAmt; - } - - public Long getPayAmt() { - return payAmt; - } - - public void setReallyAmt(Long reallyAmt) { - this.reallyAmt = reallyAmt; - } - - public Long getReallyAmt() { - return reallyAmt; - } - - public void setReceiveType(Long receiveType) { - this.receiveType = receiveType; - } - - public Long getReceiveType() { - return receiveType; - } - - public void setGoodsId(Long goodsId) { - this.goodsId = goodsId; - } - - public Long getGoodsId() { - return goodsId; - } - - public void setBusinessId(Long businessId) { - this.businessId = businessId; - } - - public Long getBusinessId() { - return businessId; - } - - public void setReceiveAddrId(Long receiveAddrId) { - this.receiveAddrId = receiveAddrId; - } - - public Long getReceiveAddrId() { - return receiveAddrId; - } - - public void setPayTime(Date payTime) { - this.payTime = payTime; - } - - public Date getPayTime() { - return payTime; - } - - public void setCreateUserId(Long createUserId) { - this.createUserId = createUserId; - } - - public Long getCreateUserId() { - return createUserId; - } - - public void setUpdateUserId(Long updateUserId) { - this.updateUserId = updateUserId; - } - - public Long getUpdateUserId() { - return updateUserId; - } - - public void setIsDelete(Long isDelete) { - this.isDelete = isDelete; - } - - public Long getIsDelete() { - return isDelete; - } - - public void setTrackNo(String trackNo) { - this.trackNo = trackNo; - } - - public String getTrackNo() { - return trackNo; - } - - public void setOrderType(Long orderType) { - this.orderType = orderType; - } - - public Long getOrderType() { - return orderType; - } - - public void setOutOrderNo(String outOrderNo) { - this.outOrderNo = outOrderNo; - } - - public String getOutOrderNo() { - return outOrderNo; - } - - public void setPayData(String payData) { - this.payData = payData; - } - - public String getPayData() { - return payData; - } - - public void setReductionAmout(Long reductionAmout) { - this.reductionAmout = reductionAmout; - } - - public Long getReductionAmout() { - return reductionAmout; - } - - @Override - public String toString() { - return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) - .append("id", getId()) - .append("orderNo", getOrderNo()) - .append("orderStatus", getOrderStatus()) - .append("payType", getPayType()) - .append("payChannel", getPayChannel()) - .append("orderAmt", getOrderAmt()) - .append("freightAmt", getFreightAmt()) - .append("payAmt", getPayAmt()) - .append("reallyAmt", getReallyAmt()) - .append("receiveType", getReceiveType()) - .append("goodsId", getGoodsId()) - .append("businessId", getBusinessId()) - .append("receiveAddrId", getReceiveAddrId()) - .append("createTime", getCreateTime()) - .append("payTime", getPayTime()) - .append("createUserId", getCreateUserId()) - .append("updateUserId", getUpdateUserId()) - .append("isDelete", getIsDelete()) - .append("updateTime", getUpdateTime()) - .append("trackNo", getTrackNo()) - .append("orderType", getOrderType()) - .append("outOrderNo", getOutOrderNo()) - .append("payData", getPayData()) - .append("reductionAmout", getReductionAmout()) - .toString(); - } } diff --git a/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java b/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java index 6bea013..1fe724d 100644 --- a/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java +++ b/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java @@ -71,5 +71,23 @@ public class OrderListResVo { */ private Integer goodsCount; + /** + * 商品价格 + */ + private Long goodsPrice; + + /** + * * 商品类型。 + * * • 0:消耗型商品 + * * • 1:非消耗型商品 + * * • 2:自动续期订阅商品 + */ + private Integer goodsType; + + /** + * 商品编码 + */ + private String goodsCode; + } diff --git a/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java b/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java index 16d34ef..9904bb6 100644 --- a/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java +++ b/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java @@ -72,4 +72,7 @@ public interface IOrderInfoService void orderPay(Long orderId); OrderInfo selectOrderInfoByOrderNo(String orderNo); + + void insertOrder(OrderInfo orderInfo); + } diff --git a/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java b/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java index 24a723a..c628cfa 100644 --- a/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java +++ b/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java @@ -62,7 +62,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService { @Override public Long createOrder(OrderCreateDto orderCreateDto) { OrderInfo orderInfo = new OrderInfo(); - orderInfo.setOrderNo(snowflakeIdWorker.nextId() + ""); + orderInfo.setOrderNo(snowflakeIdWorker.nextId()); orderInfo.setPayType(1L); orderInfo.setReceiveType(0L); orderInfo.setOrderStatus(0L); @@ -113,6 +113,11 @@ public class OrderInfoServiceImpl implements IOrderInfoService { return orderInfoMapper.selectOrderInfoByOrderNo(orderNo); } + @Override + public void insertOrder(OrderInfo orderInfo) { + orderInfoMapper.insertOrderInfo(orderInfo); + } + /** * 批量删除订单基础信息 * diff --git a/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml b/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml index 51117d1..bab236b 100644 --- a/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml +++ b/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml @@ -42,13 +42,16 @@ + + + select id, order_no, order_status, pay_type, pay_channel, order_amt, freight_amt, pay_amt, really_amt, receive_type, goods_id, business_id, receive_addr_id, create_time, pay_time, create_user_id, update_user_id, is_delete, update_time, track_no, order_type, out_order_no, pay_data, reduction_amout from Order_info - SELECT a.id,a.order_no,a.order_status,a.order_amt,a.pay_time,a.count as goods_count,a.subscription_cancellation_time,b.product_title,b.product_picture,b.product_desc,b.goods_spec + SELECT a.id,a.order_no,a.order_status,a.order_amt,a.pay_time,a.goods_count,a.subscription_cancellation_time,a.goods_type,a.goods_price,a.goods_code,b.product_title,b.product_picture,b.product_desc,b.goods_spec FROM Order_info a LEFT JOIN GOODS_MESSAGES b ON a.goods_id = b.id @@ -124,6 +127,10 @@ out_order_no, pay_data, reduction_amout, + goods_count, + goods_type, + goods_price, + goods_code, #{id}, @@ -150,6 +157,10 @@ #{outOrderNo}, #{payData}, #{reductionAmout}, + #{goodsCount}, + #{goodsType}, + #{goodsPrice}, + #{goodsCode}, diff --git a/sf-payment/pom.xml b/sf-payment/pom.xml index b8bc13d..680816a 100644 --- a/sf-payment/pom.xml +++ b/sf-payment/pom.xml @@ -30,11 +30,20 @@ com.smarterFramework sf-order + + com.smarterFramework + sf-service + org.bouncycastle bcprov-jdk18on 1.73 + + com.auth0 + java-jwt + 4.4.0 + diff --git a/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java b/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java index e52acb7..f9d76f5 100644 --- a/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java +++ b/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java @@ -16,7 +16,7 @@ import org.springframework.context.annotation.Configuration; public class HuaweiPaymentConfig { /** - * 客户端id:对应各平台的appKey + * 客户端id:(oauth用) */ @Value("${huawei.payment.clientId:110693217}") private String clientId; @@ -27,5 +27,11 @@ public class HuaweiPaymentConfig { @Value("${huawei.payment.clientSecret:1410c01bc71c7ba587175ae79e500137c70945acc1416a38127cf98a09a6f8ba}") private String clientSecret; + /** + * 应用id + */ + @Value("${huawei.payment.appId:5765880207854169373}") + private String appId; + } diff --git a/sf-payment/src/main/java/com/sf/payment/constant/GoodsConstants.java b/sf-payment/src/main/java/com/sf/payment/constant/GoodsConstants.java new file mode 100644 index 0000000..53b8b1c --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/constant/GoodsConstants.java @@ -0,0 +1,29 @@ +package com.sf.payment.constant; + +import io.jsonwebtoken.Claims; + +/** + * 商品信息 + * + * @author zoukun + */ +public class GoodsConstants { + /** + * 商品类型。 + * • 0:消耗型商品 + */ + public static final Integer GOODS_TYPE_CONSUMABLE = 0; + + /** + * 商品类型。 + * • 1:非消耗型商品 + */ + public static final Integer GOODS_TYPE_NON_CONSUMABLE = 1; + + /** + * 商品类型。 + * • 2:自动续期订阅商品 + */ + public static final Integer GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION = 2; + +} diff --git a/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java b/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java index baa770d..74650cc 100644 --- a/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java +++ b/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java @@ -2,11 +2,13 @@ package com.sf.payment.controller; import com.alibaba.fastjson2.JSONObject; import com.sf.common.core.domain.AjaxResult; +import com.sf.payment.constant.GoodsConstants; import com.sf.payment.domain.HuaweiPaymentCallback; import com.sf.payment.domain.HuaweiPurchasesVerifyDTO; import com.sf.payment.service.IHuaweiPaymentService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -36,7 +38,7 @@ public class HuaweiPaymentController { /** * 华为购买验证 */ - @RequestMapping("/huawei/purchases/verify") + @PostMapping("/huawei/purchases/verify") public AjaxResult purchasesVerify(@Validated @RequestBody HuaweiPurchasesVerifyDTO verifyDTO) { log.info("进入/huawei/purchases/tokens/verify: params:" + JSONObject.toJSONString(verifyDTO)); huaweiPaymentService.purchasesVerify(verifyDTO); diff --git a/sf-payment/src/main/java/com/sf/payment/domain/AuthToken.java b/sf-payment/src/main/java/com/sf/payment/domain/AuthToken.java deleted file mode 100644 index 20bd856..0000000 --- a/sf-payment/src/main/java/com/sf/payment/domain/AuthToken.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.sf.payment.domain; - -import lombok.*; - -import java.io.Serializable; - -/** - * 授权所需的token - * - * @author zoukun - */ -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AuthToken implements Serializable { - private String accessToken; - private int expireIn; - private String refreshToken; - private int refreshTokenExpireIn; - private String uid; - private String openId; - private String accessCode; - private String unionId; - - /** - * 华为返回 生成的Access Token中包含的scope。 - */ - private String scope; - - /** - * 华为返回 固定返回Bearer,标识返回Access Token的类型 - */ - private String tokenType; - - /** - * 华为返回 返回JWT格式数据,包含用户基本帐号、用户邮箱等信息。 - * 参照https://developer.huawei.com/consumer/cn/doc/HMSCore-References/account-verify-id-token_hms_reference-0000001050050577#section3142132691914 - */ - private String idToken; -} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/AuthUser.java b/sf-payment/src/main/java/com/sf/payment/domain/AuthUser.java deleted file mode 100644 index 03eaa4b..0000000 --- a/sf-payment/src/main/java/com/sf/payment/domain/AuthUser.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.sf.payment.domain; - -import com.alibaba.fastjson2.JSONObject; -import lombok.*; - -import java.io.Serializable; - -/** - * 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同 - * - * @author zoukun - */ -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AuthUser implements Serializable { - /** - * 用户第三方系统的唯一id。 - */ - private String uuid; - /** - * 用户名 - */ - private String username; - /** - * 用户昵称 - */ - private String nickname; - /** - * 用户头像 - */ - private String avatar; - /** - * 用户网址 - */ - private String blog; - /** - * 所在公司 - */ - private String company; - /** - * 位置 - */ - private String location; - /** - * 用户邮箱 - */ - private String email; - /** - * 用户手机号 - */ - private String mobileNumber; - /** - * 用户备注(各平台中的用户个人介绍) - */ - private String remark; - /** - * 用户来源 - */ - private String source; - /** - * 用户授权的token信息 - */ - private AuthToken token; - /** - * 第三方平台返回的原始用户信息 - */ - private JSONObject rawUserInfo; - - -} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java index 563f879..49a250c 100644 --- a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java @@ -21,31 +21,33 @@ public class HuaweiPurchasesVerifyDTO implements Serializable { /** - * 商品类别,取值包括: - * - * 0:消耗型商品 - * 1:非消耗型商品 - * 2:订阅型商品 + * 商品类型。 + * • 0:消耗型商品 + * • 1:非消耗型商品 + * • 2:自动续期订阅商品 */ @NotNull - private Integer kind; + private Integer type; /** - * 待下发商品的购买Token,发起购买和查询待消费商品信息时均会返回purchaseToken参数。 + * 包含订单信息的JWS格式数据。 + * 可参见对返回结果验签 + * 解码验签获取相关购买数据的JSON字符串, + * 其包含的参数请参见PurchaseOrderPayload。 */ - @NotBlank(message = "待下发商品的购买Token不能为空") - private String purchaseToken; + private String jwsPurchaseOrder; /** - * 待下发商品ID。商品ID来源于您在AppGallery Connect中配置商品信息时设置的商品ID。 + * 包含订阅状态信息的 + * JWS格式数据。 + * 可参见对返回结果验签 + * 解码验签获取相关订阅状态 + * 信息的JSON字符串, + * 其包含的参数请参见 + * SubGroupStatusPayload。 */ - @NotBlank(message = "待下发商品ID不能为空") - private String productId; + private String jwsSubscriptionStatus; + - /** - * 订单号 - */ - @NotBlank(message = "订单号不能为空") - private String orderNo; } diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiQueryResponse.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiQueryResponse.java new file mode 100644 index 0000000..4814e9f --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiQueryResponse.java @@ -0,0 +1,35 @@ +package com.sf.payment.domain; + +import lombok.Data; + +/** + * 功能描述: + * + * @author a_kun + * @date 2024/4/17 14:41 + */ +@Data +public class HuaweiQueryResponse { + + /** + * 返回码。 + * • 0:成功。 + * • 其他:失败,具体请参见错误码 + */ + private String responseCode; + + /** + * 响应描述。 + */ + private String responseMessage; + + /** + * 包含已购订单相关状态信息的JWS格式数据。可参见对返回结果验签解码验签获取相关订单状态信息的JSON字符串,其包含的参数具体请参见表PurchaseOrderPayload说明。 + */ + private String jwsPurchaseOrder; + + /** + * 包含已购订阅相关状态信息的JWS格式数据。可参见对返回结果验签解码验签获取相关订阅状态信息的JSON字符串,其包含的参数请参见SubGroupStatusPayload + */ + private String jwsSubGroupStatus; +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/PurchaseOrderPayload.java b/sf-payment/src/main/java/com/sf/payment/domain/PurchaseOrderPayload.java new file mode 100644 index 0000000..8c8fab2 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/PurchaseOrderPayload.java @@ -0,0 +1,165 @@ +package com.sf.payment.domain; + +import lombok.Data; + +/** + * 功能描述: + * 订单信息模型,支持消耗型商品、非消耗型商品和自动续期订阅商品。 + * @author a_kun + * @date 2024/4/16 15:29 + */ +@Data +public class PurchaseOrderPayload { + + /** + * 具体一笔订单中对应的购买订单号ID。 + */ + private String purchaseOrderId; + + /** + * 购买token, + * 在购买消耗型/非消耗型商品场景中与具体购买订单一一对应, + * 在自动续期订阅商品场景中与订阅ID一一对应。 + */ + private String purchaseToken; + + /** + * 应用ID。 + */ + private String applicationId; + + /** + * 商品ID。每种商品必须有唯一的ID,由应用在PMS中维护,或者应用发起购买时传入。 + * 说明 + * 为避免资金损失,您在对支付结果验签成功后,必须对其进行校验。 + */ + private String productId; + + /** + * 商品类型。具体取值如下: + * • 0:消耗型商品 + * • 1:非消耗型商品 + * • 2:自动续期订阅商品 + */ + private Integer productType; + + /** + * 购买时间,UTC时间戳,以毫秒为单位。 + * 如果没有完成购买,则没有值。 + */ + private Long purchaseTime; + + /** + * 发货状态。具体取值如下: + * • 1:已发货 + * • 2:未发货 + */ + private Integer finishStatus; + + /** + * 价格,单位:分。 + * 实际价格*100以后的值。商品实际价格精确到小数点后2位,例如此参数值为501,则表示商品实际价格为5.01。 + */ + private Long price; + + /** + * 用定价货币的币种,请参见ISO 4217标准。 + * 说明 + * 为避免资金损失,您在对支付结果验签成功后,必须对其进行校验。 + */ + private String currency; + + /** + * 商户侧保留信息,由您在调用支付接口时传入。 + */ + private String developerPayload; + + /** + * 购买订单撤销原因。 + * • 0:其他 + * • 1:用户遇到问题退款 + */ + private Integer purchaseOrderRevocationReasonCode; + + /** + * 购买订单撤销时间, + * UTC时间戳,以毫秒为单位。 + */ + private Long revocationTime; + + /** + * 优惠类型。 + * • 1:推介促销 + */ + private Integer offerTypeCode; + + /** + * 优惠ID。 + */ + private String offerId; + + /** + * 国家/地区码,用于区分国家/地区信息,请参见ISO 3166 + */ + private String countryCode; + + /** + * 签名时间,UTC时间戳,以毫秒为单位。 + */ + private Long signedTime; + + // 以下参数只在自动续期订阅商品场景返回 + + + /** + * 订阅连续购买段的唯一ID, + * 当用户切换商品不会重置此ID。 + */ + private String subGroupGenerationId; + + /** + * 订阅连续购买段的唯一ID, + * 当用户切换订阅商品时 + * 此订阅ID会发生改变。 + */ + private String subscriptionId; + + /** + * 订阅组ID。 + */ + private String subGroupId; + + /** + * 此次购买的有效周期, + * 采用ISO 8601格式。 + * 例如:P1W表示一周, + * P1M表示一个月。 + */ + private String duration; + + /** + * 订阅周期段类型。 + * • 0:正常周期段 + * • 1:延期周期段 + */ + private Integer durationTypeCode; + + /* + ISO 8601的时间持续期限表示 + 在ISO 8601中,时间持续期限的表示采用了一种简洁而明确的格式,例如 “P10D”,其中 “P” 表示周期(Period),后面的数字表示周期的长度,而末尾的字母表示周期的单位。这种表示法主要用于描述时间段的长度,而不关注具体的时刻。 + “P” 表示周期(Period): 此字母指示接下来的时间表示将是一个时间段的描述,而非具体的日期或时刻。 + 后面的数字: 这个数字表示时间段的长度,可以是整数或小数。它指示了在时间单位内的周期数量。 + 末尾的字母表示周期的单位: 在 “P10D” 中,末尾的 “D” 表示周期的单位是天(Days)。ISO 8601定义了多种可能的时间单位,包括: + Y(年): 表示年份,例如 “P2Y” 表示2年的时间段。 + M(月): 表示月份,例如 “P3M” 表示3个月的时间段。 + W(周): 表示周数,例如 “P1W” 表示1周的时间段。 + D(日): 表示天数,例如 “P10D” 表示10天的时间段。 + T(时间分隔符): 如果时间段中包含了时间信息,日期和时间之间用 “T” 分隔,例如 “P1DT12H” 表示1天12小时的时间段。 + H(小时)、M(分钟)、S(秒): 用于表示时、分、秒的时间段长度,例如 “PT2H30M” 表示2小时30分钟的时间段。 + 示例: + “P1Y”: 表示1年的时间段。 + “P3M”: 表示3个月的时间段。 + “P2W”: 表示2周的时间段。 + “P4DT6H30M”: 表示4天6小时30分钟的时间段。 + */ +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/SubGroupStatusPayload.java b/sf-payment/src/main/java/com/sf/payment/domain/SubGroupStatusPayload.java new file mode 100644 index 0000000..7fcad45 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/SubGroupStatusPayload.java @@ -0,0 +1,48 @@ +package com.sf.payment.domain; + +import lombok.Data; + +import java.util.List; + +/** + * 功能描述: + * 已购订阅相关状态信息 + * @author a_kun + * @date 2024/4/16 15:29 + */ +@Data +public class SubGroupStatusPayload { + + /** + * 应用ID。 + */ + private String applicationId; + + /** + * 应用包名。 + */ + private String packageName; + + /** + * 订阅组ID。 + */ + private String subGroupId; + + /** + * 订阅组中最后生效的 + * 订阅状态 + * SubscriptionStatus, + * 比如A切换B,B切换C, + * 此处是C的订阅状态。 + */ + private SubscriptionStatus lastSubscriptionStatus; + + /** + * 订阅组最近生效的 + * 历史订阅状态 + * SubscriptionStatus的列表,比如A切换B,B切换C,这里包含C,B,A三个订阅状态信息。 + */ + private List historySubscriptionStatusList; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/SubRenewalInfo.java b/sf-payment/src/main/java/com/sf/payment/domain/SubRenewalInfo.java new file mode 100644 index 0000000..cfff488 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/SubRenewalInfo.java @@ -0,0 +1,91 @@ +package com.sf.payment.domain; + +import lombok.Data; + +import java.util.List; + +/** + * 功能描述: + * 当前订阅最新的未来扣费计划 + * @author a_kun + * @date 2024/4/16 15:29 + */ +@Data +public class SubRenewalInfo { + + /** + * 订阅连续购买段的唯一ID, + * 当用户切换商品不会重置此ID。 + */ + private String subGroupGenerationId; + + /** + * 下周期生效场景下,下期将续期的商品ID。 + */ + private String nextRenewPeriodProductId; + + /** + * 当前生效的商品ID。 + */ + private String productId; + + /** + * 自动续期状态。 + * • 0:关闭 + * • 1:打开 + */ + private Integer autoRenewStatusCode; + + /** + * 系统是否还在尝试扣费。 + * • true:是 + * • false:否 + */ + private Boolean hasInBillingRetryPeriod; + + /** + * 目前涨价状态码。 + * • 1:用户暂未同意涨价 + * • 2:用户已同意涨价 + */ + private Integer priceIncreaseStatusCode; + + /** + * 优惠类型。 + * • 1:推介促销 + */ + private Integer offerTypeCode; + + /** + * 优惠ID。 + */ + private String offerId; + + /** + * 下期续费价格,单位:分,取消订阅场景下不返回。 + */ + private String renewalPrice; + + /** + * 币种。 + */ + private String currency; + + /** + * 续期时间,UTC时间戳,以毫秒为单位。。 + */ + private Long renewalTime; + + /** + * 订阅续期失败的原因。 + * • 1:用户取消 + * • 2:商品无效 + * • 3:签约无效 + * • 4:扣费异常 + * • 5:用户不同意涨价 + * • 6:未知 + */ + private String expirationIntent; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/SubscriptionStatus.java b/sf-payment/src/main/java/com/sf/payment/domain/SubscriptionStatus.java new file mode 100644 index 0000000..8adb949 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/SubscriptionStatus.java @@ -0,0 +1,68 @@ +package com.sf.payment.domain; + +import lombok.Data; + +import java.util.List; + +/** + * 功能描述: + * 订阅组中最后生效的订阅状态 + * @author a_kun + * @date 2024/4/16 15:29 + */ +@Data +public class SubscriptionStatus { + + /** + * 订阅连续购买段的唯一ID, + * 当用户切换商品不会重置此ID。 + */ + private String subGroupGenerationId; + + /** + * 订阅连续购买段的唯一ID, + * 当用户切换订阅商品时 + * 此订阅ID会发生改变。 + */ + private String subscriptionId; + + /** + * 购买token, + * 在购买消耗型/非消耗型商品场景中与具体购买订单一一对应, + * 在自动续期订阅商品场景中与订阅ID一一对应。 + */ + private String purchaseToken; + + /** + * 订阅状态。 + * • 1:生效状态 + * • 2:已到期 + * • 3:尝试扣费 + * • 5:撤销 + * • 6:暂停 + */ + private Integer status; + + /** + * 自动续期订阅商品的过期时间,UTC时间戳,以毫秒为单位。 + */ + private Long expiresTime; + + /** + * 当前订阅最新的一笔购买订单。包含的参数请参见PurchaseOrderPayload。 + */ + private PurchaseOrderPayload lastPurchaseOrder; + + /** + * 当前订阅最新的购买订单列表,包含续期、延期、折算等产生的购买订单。 + * 购买订单包含的参数请参见PurchaseOrderPayload。 + */ + private List recentPurchaseOrderList; + + /** + * 当前订阅最新的未来扣费计划,包含的参数请参见SubRenewalInfo。 + */ + private SubRenewalInfo renewalInfo; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java b/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java index 2cf3347..1a86679 100644 --- a/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java +++ b/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java @@ -6,26 +6,32 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; import com.sf.common.utils.SecurityUtils; +import com.sf.common.utils.SnowflakeIdWorker; import com.sf.order.domain.OrderInfo; import com.sf.order.service.IOrderInfoService; import com.sf.payment.config.HuaweiPaymentConfig; -import com.sf.payment.domain.HuaweiPurchasesVerifyDTO; -import com.sf.payment.domain.HuaweiPurchasesVerifyResponseDTO; -import com.sf.payment.domain.InAppPurchaseData; +import com.sf.payment.constant.GoodsConstants; +import com.sf.payment.domain.*; import com.sf.payment.service.IHuaweiPaymentService; +import com.sf.payment.utils.HuaweiTokenGenerator; +import com.sf.service.domain.GoodsMessages; +import com.sf.service.service.IGoodsMessagesService; import com.sf.system.domain.UserMember; import com.sf.system.service.IUserMemberService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; +import org.springframework.util.Base64Utils; +import javax.annotation.Resource; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; +import java.security.MessageDigest; import java.security.PublicKey; import java.security.Security; import java.security.spec.X509EncodedKeySpec; @@ -45,58 +51,204 @@ import java.util.Map; @Service public class HuaweiPaymentServiceImpl implements IHuaweiPaymentService { - // token url to get the authorization + /** + * token url to get the authorization + */ private static final String TOKEN_URL = "https://oauth-login.cloud.huawei.com/oauth2/v3/token"; - private static final String VERIFY_TOKEN_URL = "https://orders-drcn.iap.cloud.huawei.com.cn/applications/purchases/tokens/verify"; + + /** + * 查询消耗型/非消耗型商品的订单最新状态。 + */ + private static final String ORDER_STATUS_QUERY_URL = "/order/harmony/v1/application/order/status/query"; + + /** + * 查询自动续期订阅商品的最新状态。 + */ + private static final String SUBSCRIPTION_STATUS_QUERY_URL = "/subscription/harmony/v1/application/subscription/status/query"; + + /** + * 站点信息。(中国) + */ + private static final String ROOT_URL = "https://iap.cloud.huawei.com"; private static final String PUBLIC_KEY = "PUBLIC_KEY"; - @Autowired + @Resource private IOrderInfoService orderInfoService; - @Autowired + @Resource private IUserMemberService userMemberService; - @Autowired + @Resource + private IGoodsMessagesService goodsMessagesService; + + @Resource private HuaweiPaymentConfig huaweiPaymentConfig; + @Resource + private SnowflakeIdWorker snowflakeIdWorker; + + @Override @Transactional(rollbackFor = Exception.class) public void purchasesVerify(HuaweiPurchasesVerifyDTO verifyDTO) { - // construct the Authorization in Header - Map headers = buildAuthorization(getAppAT(huaweiPaymentConfig.getClientId(), huaweiPaymentConfig.getClientSecret())); + // 待发放会员商品 + PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload; + // 验证 TODO 证书验签官网未实现 + if (GoodsConstants.GOODS_TYPE_CONSUMABLE.equals(verifyDTO.getType()) + || GoodsConstants.GOODS_TYPE_NON_CONSUMABLE.equals(verifyDTO.getType())) { + Assert.hasText(verifyDTO.getJwsPurchaseOrder(), "订单信息不能为空"); + // 消耗/非消耗商品购买验证 + consumablePurchasesVerify(verifyDTO.getJwsPurchaseOrder()); + } else if (GoodsConstants.GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION.equals(verifyDTO.getType())) { + Assert.hasText(verifyDTO.getJwsSubscriptionStatus(), "订单信息不能为空"); + // 订阅商品购买验证 + subscriptionPurchasesVerify(verifyDTO.getJwsSubscriptionStatus()); + } else { + throw new IllegalArgumentException("商品类型错误!"); + } + } + private PurchaseOrderPayload subscriptionPurchasesVerify(String jwsSubscriptionStatus) { + DecodedJWT decodedJWT = JWT.decode(jwsSubscriptionStatus); + //String header = decodedJWT.getHeader(); + String payload = decodedJWT.getPayload(); + // 前面应该不是base64编码后的,官网说都是编码后的,但是解码会报错 + //String signature = decodedJWT.getSignature(); + String decodeAppPayload = new String(Base64Utils.decode(payload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + // String decodeHeader = new String(Base64Utils.decode(header.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + // String decodeSignature = new String(Base64Utils.decode(signature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + SubGroupStatusPayload appSubGroupStatusPayload = JSON.parseObject(decodeAppPayload, SubGroupStatusPayload.class); + SubscriptionStatus lastSubscriptionStatus = appSubGroupStatusPayload.getLastSubscriptionStatus(); + PurchaseOrderPayload lastPurchaseOrder = lastSubscriptionStatus.getLastPurchaseOrder(); // pack the request body Map bodyMap = new HashMap<>(); - bodyMap.put("purchaseToken", verifyDTO.getPurchaseToken()); - bodyMap.put("productId", verifyDTO.getProductId()); + bodyMap.put("purchaseToken", lastPurchaseOrder.getPurchaseToken()); + bodyMap.put("purchaseOrderId", lastPurchaseOrder.getPurchaseOrderId()); + // construct the Authorization in Header + Map headers = buildAuthorization(huaweiPaymentConfig.getAppId(), bodyMap); - String response = HttpUtil.createPost(VERIFY_TOKEN_URL) + // 订阅状态查询 + String response = HttpUtil.createPost(ROOT_URL + SUBSCRIPTION_STATUS_QUERY_URL) .addHeaders(headers) .body(JSON.toJSONString(bodyMap)) .execute().body(); - HuaweiPurchasesVerifyResponseDTO huaweiPurchasesVerifyResponseDTO = JSON.parseObject(response, HuaweiPurchasesVerifyResponseDTO.class); - InAppPurchaseData inAppPurchaseData = JSON.parseObject(huaweiPurchasesVerifyResponseDTO.getPurchaseTokenData(), InAppPurchaseData.class); - // 获取服务订单 - OrderInfo orderInfo = orderInfoService.selectOrderInfoByOrderNo(verifyDTO.getOrderNo()); - // 校验订单 - boolean checkSuccessOrder = checkSuccessOrder(huaweiPurchasesVerifyResponseDTO.getPurchaseTokenData() - , huaweiPurchasesVerifyResponseDTO.getDataSignature() - , PUBLIC_KEY - , huaweiPurchasesVerifyResponseDTO.getSignatureAlgorithm() - , orderInfo); + log.info("订单状态查询返回信息:{}", response); + HuaweiQueryResponse huaweiQueryResponse = JSON.parseObject(response, HuaweiQueryResponse.class); + if (!"0".equals(huaweiQueryResponse.getResponseCode())) { + throw new RuntimeException("订单状态查询失败"); + } + DecodedJWT huaweiQueryResponseDecodedJWT = JWT.decode(huaweiQueryResponse.getJwsSubGroupStatus()); + String huaweiQueryResponsepayload = huaweiQueryResponseDecodedJWT.getPayload(); + String decodeHuaweiQueryResponsepayload = new String(Base64Utils.decode(huaweiQueryResponsepayload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + SubGroupStatusPayload subGroupStatusPayload = JSON.parseObject(decodeHuaweiQueryResponsepayload, SubGroupStatusPayload.class); + PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload = subGroupStatusPayload.getLastSubscriptionStatus().getLastPurchaseOrder(); + // 发货 + return delivery(lastPurchaseOrder, huaweiQueryResponsePurchaseOrderPayload); + } - Assert.isTrue(checkSuccessOrder, "订单校验失败,请重试"); - Assert.isTrue(inAppPurchaseData.getPurchaseState()==0, "订单未完成购买"); - DateTime payTime = DateUtil.date(inAppPurchaseData.getPurchaseTime()); - UserMember userMember = userMemberService.selectUserMemberByUserId(orderInfo.getCreateUserId()); + /** + * 发货 + * @param purchaseOrder + * @param huaweiQueryResponsePurchaseOrderPayload + * @return + */ + private PurchaseOrderPayload delivery(PurchaseOrderPayload purchaseOrder, PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload) { + Assert.isTrue(purchaseOrder.getPurchaseOrderId().equals(huaweiQueryResponsePurchaseOrderPayload.getPurchaseOrderId()), "订单不一致,发货失败!"); + if (2 == huaweiQueryResponsePurchaseOrderPayload.getFinishStatus()) { + // 还未发货 + // 查询平台是否配置该商品 + // 查询平台是否配置该商品 + GoodsMessages goods = goodsMessagesService.selectGoodsMessagesByCode(huaweiQueryResponsePurchaseOrderPayload.getProductId()); + Assert.notNull(goods, "未配置此商品,请检查商品配置"); + Assert.isTrue(goods.getOriginalPrice().equals(huaweiQueryResponsePurchaseOrderPayload.getPrice()), "商品价格与订单价格不一致,请检查价格配置"); + // 创建完成订单 + createOrder(huaweiQueryResponsePurchaseOrderPayload, goods); + // 发放会员权益 + distributeMembershipBenefits(huaweiQueryResponsePurchaseOrderPayload); + } else { + log.info("华为应用内支付订单已发货!{}", JSON.toJSONString(huaweiQueryResponsePurchaseOrderPayload)); + } + return huaweiQueryResponsePurchaseOrderPayload; + } + + private PurchaseOrderPayload consumablePurchasesVerify(String jwsPurchaseOrder) { + DecodedJWT decodedJWT = JWT.decode(jwsPurchaseOrder); + //String header = decodedJWT.getHeader(); + String payload = decodedJWT.getPayload(); + // 前面应该不是base64编码后的,官网说都是编码后的,但是解码会报错 + //String signature = decodedJWT.getSignature(); + String decodeAppPayload = new String(Base64Utils.decode(payload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + // String decodeHeader = new String(Base64Utils.decode(header.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + // String decodeSignature = new String(Base64Utils.decode(signature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + PurchaseOrderPayload appPurchaseOrderPayload = JSON.parseObject(decodeAppPayload, PurchaseOrderPayload.class); + // pack the request body + Map bodyMap = new HashMap<>(); + bodyMap.put("purchaseToken", appPurchaseOrderPayload.getPurchaseToken()); + bodyMap.put("purchaseOrderId", appPurchaseOrderPayload.getPurchaseOrderId()); + // construct the Authorization in Header + Map headers = buildAuthorization(huaweiPaymentConfig.getAppId(), bodyMap); + + // 订单状态查询 + String response = HttpUtil.createPost(ROOT_URL + ORDER_STATUS_QUERY_URL) + .addHeaders(headers) + .body(JSON.toJSONString(bodyMap)) + .execute().body(); + log.info("订单状态查询返回信息:{}", response); + HuaweiQueryResponse huaweiQueryResponse = JSON.parseObject(response, HuaweiQueryResponse.class); + if (!"0".equals(huaweiQueryResponse.getResponseCode())) { + throw new RuntimeException("订单状态查询失败"); + } + DecodedJWT huaweiQueryResponseDecodedJWT = JWT.decode(huaweiQueryResponse.getJwsPurchaseOrder()); + // String huaweiQueryResponseHeader = huaweiQueryResponseDecodedJWT.getHeader(); + String huaweiQueryResponsepayload = huaweiQueryResponseDecodedJWT.getPayload(); + // String huaweiQueryResponsesignature = huaweiQueryResponseDecodedJWT.getSignature(); + + //String decodehuaweiQueryResponseHeader = new String(Base64Utils.decode(huaweiQueryResponseHeader.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + String decodehuaweiQueryResponsepayload = new String(Base64Utils.decode(huaweiQueryResponsepayload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + // String decodehuaweiQueryResponsesignature = new String(Base64Utils.decode(huaweiQueryResponsesignature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + + PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload = JSON.parseObject(decodehuaweiQueryResponsepayload, PurchaseOrderPayload.class); + // 暂时只做简单验证 + return delivery(appPurchaseOrderPayload, huaweiQueryResponsePurchaseOrderPayload); + } + + private void createOrder(PurchaseOrderPayload appPurchaseOrderPayload, GoodsMessages goods) { + Long userId = SecurityUtils.getUserId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setOrderNo(snowflakeIdWorker.nextId()); + orderInfo.setOrderStatus(6L); + orderInfo.setPayType(1L); + orderInfo.setPayChannel(2L); + orderInfo.setOrderAmt(appPurchaseOrderPayload.getPrice()); + orderInfo.setPayAmt(appPurchaseOrderPayload.getPrice()); + orderInfo.setReallyAmt(appPurchaseOrderPayload.getPrice()); + orderInfo.setReceiveType(0L); + orderInfo.setGoodsId(goods.getId()); + orderInfo.setPayTime(DateUtil.date(appPurchaseOrderPayload.getPurchaseTime())); + orderInfo.setCreateUserId(userId); + orderInfo.setUpdateUserId(userId); + orderInfo.setOutOrderNo(appPurchaseOrderPayload.getPurchaseOrderId()); + orderInfo.setPayData(JSON.toJSONString(appPurchaseOrderPayload)); + orderInfo.setGoodsPrice(goods.getOriginalPrice()); + orderInfo.setGoodsType(goods.getGoodsType()); + orderInfo.setGoodsCode(goods.getGoodsCode()); + orderInfoService.insertOrder(orderInfo); + + } + + private void distributeMembershipBenefits(PurchaseOrderPayload purchaseOrderPayload) { + // 发放会员权益 + Long userId = SecurityUtils.getUserId(); + UserMember userMember = userMemberService.selectUserMemberByUserId(userId); if (userMember == null) { // 添加会员信息 - boolean isSubscription = verifyDTO.getKind() == 2; + boolean isSubscription = GoodsConstants.GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION.equals(purchaseOrderPayload.getProductType()); + DateTime payTime = DateUtil.date(purchaseOrderPayload.getPurchaseTime()); userMember = new UserMember(); userMember.setMemberLevel(isSubscription ? 1 : 2); userMember.setSubscriptionStatus(isSubscription ? 1 : 0); - userMember.setUserId(orderInfo.getCreateUserId()); + userMember.setUserId(userId); userMember.setIntegration(0L); DateTime expirationTime = DateUtil.offset(payTime, DateField.MONTH, 1); userMember.setExpirationTime(expirationTime); @@ -110,48 +262,56 @@ public class HuaweiPaymentServiceImpl implements IHuaweiPaymentService { userMember.setUpdateTime(new Date()); userMemberService.updateUserMember(userMember); } - // 更新订单状态 - orderInfo.setPayTime(payTime); - orderInfo.setOrderStatus(3L); - orderInfo.setPayChannel(2L); - orderInfo.setPayAmt(orderInfo.getOrderAmt()); - orderInfo.setReallyAmt(orderInfo.getOrderAmt()); - orderInfo.setOrderStatus(3L); - orderInfo.setPayData(huaweiPurchasesVerifyResponseDTO.getPurchaseTokenData()); - orderInfoService.updateOrderInfo(orderInfo); } - /** - * Gets App Level AccessToken. - */ - public static String getAppAT(String clientId, String clientSecret) { - // fetch accessToken - Map form = new HashMap<>(8); - form.put("grant_type", "client_credentials"); - form.put("client_secret", clientSecret); - form.put("client_id", clientId); - String atResponse = HttpUtil.post(TOKEN_URL, form); - log.info("getAppAT Response : {}", atResponse); - JSONObject parseObject = JSON.parseObject(atResponse); - return parseObject.getString("access_token"); - } /** * Build Authorization in Header * - * @param appAt appAt * @return headers */ - public static Map buildAuthorization(String appAt) { - String oriString = MessageFormat.format("APPAT:{0}", appAt); - String authorization = - MessageFormat.format("Basic {0}", Base64.encodeBase64String(oriString.getBytes(StandardCharsets.UTF_8))); + public static Map buildAuthorization(String appId, Map body) { + Map jwtHeader = new HashMap<>(8); + jwtHeader.put("alg", "ES256"); + jwtHeader.put("typ", "JWT"); + jwtHeader.put("kid", "0ae3e1be-374b-43a5-a297-045addbf76eb"); + Map jwtPayload = new HashMap<>(8); + jwtPayload.put("iss", "f59509e6-dd17-4644-b832-ff05233146c8"); + jwtPayload.put("aud", "iap-v1"); + jwtPayload.put("iat", DateUtil.currentSeconds()); + jwtPayload.put("exp", DateUtil.currentSeconds() + 1800L); // 半小时过期 + jwtPayload.put("aid", appId); + jwtPayload.put("digest", getJwtPayloadDigest(body)); + + String token = HuaweiTokenGenerator.createToken(jwtHeader, jwtPayload); + // String authorization = MessageFormat.format("Basic {0}", Base64.encodeBase64String(oriString.getBytes(StandardCharsets.UTF_8))); + String authorization = MessageFormat.format("Bearer {0}", token); Map headers = new HashMap<>(); headers.put("Authorization", authorization); headers.put("Content-Type", "application/json; charset=UTF-8"); return headers; } + private static String getJwtPayloadDigest(Map body) { + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(JSON.toJSONString(body).getBytes(StandardCharsets.UTF_8)); + byte[] digestByte = messageDigest.digest(); + StringBuilder stringBuffer = new StringBuilder(); + String temp; + for (byte aByte : digestByte) { + temp = Integer.toHexString(aByte & 0xFF); + if (temp.length() == 1) { + stringBuffer.append("0"); + } + stringBuffer.append(temp); + } + return stringBuffer.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** * 校验签名信息,校验InAppPurchaseData中的productId、price、currency等信息的一致性 * @@ -173,15 +333,15 @@ public class HuaweiPaymentServiceImpl implements IHuaweiPaymentService { /** * 校验InAppPurchaseData中的productId、price、currency等信息的一致性 * - * @param content 结果字符串 - * @param yourOrderInfo 您的订单信息,包括productId、price、currency + * @param content 结果字符串 + * @param orderInfo 您的订单信息,包括productId、price、currency * @return 是否校验通过 */ - public static boolean checkProductIdAndPriceAndCurrency(String content, OrderInfo yourOrderInfo) { + public static boolean checkProductIdAndPriceAndCurrency(String content, OrderInfo orderInfo) { InAppPurchaseData inAppPurchaseData = JSON.parseObject(content, InAppPurchaseData.class); // 校验InAppPurchaseData中的productId、price、currency等信息的一致性 - return inAppPurchaseData.getProductId().equals(yourOrderInfo.getGoodsId()) - && inAppPurchaseData.getPrice().equals(yourOrderInfo.getOrderAmt()); + return inAppPurchaseData.getProductId().equals(orderInfo.getGoodsId()) + && inAppPurchaseData.getPrice().equals(orderInfo.getOrderAmt()); } /** diff --git a/sf-payment/src/main/java/com/sf/payment/utils/HuaweiTokenGenerator.java b/sf-payment/src/main/java/com/sf/payment/utils/HuaweiTokenGenerator.java new file mode 100644 index 0000000..4572ea6 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/utils/HuaweiTokenGenerator.java @@ -0,0 +1,43 @@ +package com.sf.payment.utils; +import cn.hutool.core.io.resource.ClassPathResource; +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +import java.util.Map; + +public class HuaweiTokenGenerator { + + public static String createToken(Map jwtHeader, Map jwtPayload) { + try { + // AppGallery Connect 华为应用内支付配置密钥,下载私钥文件 + ClassPathResource classPathResource = new ClassPathResource("IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8"); + InputStream IAPKeyStream = classPathResource.getStream(); + String content = IOUtils.toString(IAPKeyStream, String.valueOf(StandardCharsets.UTF_8)); + String privateKey = content + .replace("-----BEGIN PRIVATE KEY-----", "") + .replaceAll("\\R+", "") + .replace("-----END PRIVATE KEY-----", ""); + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + ECPrivateKey ecPrivateKey = (ECPrivateKey) keyFactory.generatePrivate(keySpec); + return JWT.create() + .withHeader(jwtHeader) + .withPayload(jwtPayload) + .sign(Algorithm.ECDSA256(ecPrivateKey)); + } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + } +} diff --git a/sf-payment/src/main/resources/IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8 b/sf-payment/src/main/resources/IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8 new file mode 100644 index 0000000..e1ebbeb --- /dev/null +++ b/sf-payment/src/main/resources/IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8 @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQggG04243qynU/yWYy +XpYVy9ZWMuLKzZiwhXCBWQBCOLigCgYIKoZIzj0DAQehRANCAARNln2/d+TM2pIO +LWQzvI77gPAVEvVCSlIuiJ+J7CJSG5KCysBaEeiiD5cc4dZWnUBijF8FBh7nDLaH +VwFXfrS+ +-----END PRIVATE KEY----- diff --git a/sf-service/pom.xml b/sf-service/pom.xml new file mode 100644 index 0000000..84ffb42 --- /dev/null +++ b/sf-service/pom.xml @@ -0,0 +1,33 @@ + + + + smarterFramework + com.smarterFramework + 1.0.0 + + 4.0.0 + + sf-service + + + 业务模块 + + + + + + + com.smarterFramework + sf-common + + + com.smarterFramework + sf-framework + + + + + + \ No newline at end of file diff --git a/sf-admin/src/main/java/com/sf/goods/controller/GoodsMessagesController.java b/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java similarity index 83% rename from sf-admin/src/main/java/com/sf/goods/controller/GoodsMessagesController.java rename to sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java index cd2ca0a..4117dab 100644 --- a/sf-admin/src/main/java/com/sf/goods/controller/GoodsMessagesController.java +++ b/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java @@ -1,7 +1,10 @@ -package com.sf.goods.controller; +package com.sf.service.controller; import java.util.List; import javax.servlet.http.HttpServletResponse; + +import com.sf.service.domain.GoodsMessages; +import com.sf.service.service.IGoodsMessagesService; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; @@ -16,19 +19,17 @@ import com.sf.common.annotation.Log; import com.sf.common.core.controller.BaseController; import com.sf.common.core.domain.AjaxResult; import com.sf.common.enums.BusinessType; -import com.sf.goods.domain.GoodsMessages; -import com.sf.goods.service.IGoodsMessagesService; import com.sf.common.utils.poi.ExcelUtil; import com.sf.common.core.page.TableDataInfo; /** * 商品信息Controller - * - * @author ztzh - * @date 2024-04-11 + * + * @author zoukun + * @date 2024-04-18 */ @RestController -@RequestMapping("/goods/goods") +@RequestMapping("/service/goods") public class GoodsMessagesController extends BaseController { @Autowired @@ -37,7 +38,7 @@ public class GoodsMessagesController extends BaseController /** * 查询商品信息列表 */ - @PreAuthorize("@ss.hasPermi('goods:goods:list')") + @PreAuthorize("@ss.hasPermi('service:goods:list')") @GetMapping("/list") public TableDataInfo list(GoodsMessages goodsMessages) { @@ -49,7 +50,7 @@ public class GoodsMessagesController extends BaseController /** * 导出商品信息列表 */ - @PreAuthorize("@ss.hasPermi('goods:goods:export')") + @PreAuthorize("@ss.hasPermi('service:goods:export')") @Log(title = "商品信息", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, GoodsMessages goodsMessages) @@ -62,7 +63,7 @@ public class GoodsMessagesController extends BaseController /** * 获取商品信息详细信息 */ - @PreAuthorize("@ss.hasPermi('goods:goods:query')") + @PreAuthorize("@ss.hasPermi('service:goods:query')") @GetMapping(value = "/{id}") public AjaxResult getInfo(@PathVariable("id") Long id) { @@ -72,7 +73,7 @@ public class GoodsMessagesController extends BaseController /** * 新增商品信息 */ - @PreAuthorize("@ss.hasPermi('goods:goods:add')") + @PreAuthorize("@ss.hasPermi('service:goods:add')") @Log(title = "商品信息", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody GoodsMessages goodsMessages) @@ -83,7 +84,7 @@ public class GoodsMessagesController extends BaseController /** * 修改商品信息 */ - @PreAuthorize("@ss.hasPermi('goods:goods:edit')") + @PreAuthorize("@ss.hasPermi('service:goods:edit')") @Log(title = "商品信息", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody GoodsMessages goodsMessages) @@ -94,9 +95,9 @@ public class GoodsMessagesController extends BaseController /** * 删除商品信息 */ - @PreAuthorize("@ss.hasPermi('goods:goods:remove')") + @PreAuthorize("@ss.hasPermi('service:goods:remove')") @Log(title = "商品信息", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") + @DeleteMapping("/{ids}") public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(goodsMessagesService.deleteGoodsMessagesByIds(ids)); diff --git a/sf-admin/src/main/java/com/sf/goods/domain/GoodsMessages.java b/sf-service/src/main/java/com/sf/service/domain/GoodsMessages.java similarity index 79% rename from sf-admin/src/main/java/com/sf/goods/domain/GoodsMessages.java rename to sf-service/src/main/java/com/sf/service/domain/GoodsMessages.java index 16a24a5..86c36bb 100644 --- a/sf-admin/src/main/java/com/sf/goods/domain/GoodsMessages.java +++ b/sf-service/src/main/java/com/sf/service/domain/GoodsMessages.java @@ -1,4 +1,4 @@ -package com.sf.goods.domain; +package com.sf.service.domain; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -8,8 +8,8 @@ import com.sf.common.core.domain.BaseEntity; /** * 商品信息对象 GOODS_MESSAGES * - * @author ztzh - * @date 2024-04-11 + * @author zoukun + * @date 2024-04-18 */ public class GoodsMessages extends BaseEntity { @@ -26,6 +26,7 @@ public class GoodsMessages extends BaseEntity private Long stockId; /** 审核状态,1通过,0未通过 */ + @Excel(name = "审核状态,1通过,0未通过") private Long reviewStatus; /** 商品标题 */ @@ -36,16 +37,23 @@ public class GoodsMessages extends BaseEntity @Excel(name = "商品图片") private String productPicture; - /** 原价格 */ - @Excel(name = "原价格") + /** 商品原价 */ + @Excel(name = "商品原价") private Long originalPrice; /** 商品描述 */ @Excel(name = "商品描述") private String productDesc; + /** 商品类型。 * • 0:消耗型商品 * • 1:非消耗型商品 * • 2:自动续期订阅商品 */ + @Excel(name = "商品类型。 * • 0:消耗型商品 * • 1:非消耗型商品 * • 2:自动续期订阅商品") + private Integer goodsType; + + /** 商品规格 */ + @Excel(name = "商品规格") + private String goodsSpec; + /** 排序 */ - @Excel(name = "排序") private Long orderNum; /** 逻辑删除,0:未删除,1:删除 */ @@ -57,13 +65,12 @@ public class GoodsMessages extends BaseEntity /** 更新人 */ private String modified; - /** 商品类型 */ - @Excel(name = "商品类型") - private String goodsType; + /** 商品名称 */ + private String goodsName; - /** 商品规格 */ - @Excel(name = "商品规格") - private String goodsSpec; + /** 商品型号 */ + @Excel(name = "商品型号") + private String goodsModel; public void setId(Long id) { @@ -137,6 +144,24 @@ public class GoodsMessages extends BaseEntity { return productDesc; } + public void setGoodsType(Integer goodsType) + { + this.goodsType = goodsType; + } + + public Integer getGoodsType() + { + return goodsType; + } + public void setGoodsSpec(String goodsSpec) + { + this.goodsSpec = goodsSpec; + } + + public String getGoodsSpec() + { + return goodsSpec; + } public void setOrderNum(Long orderNum) { this.orderNum = orderNum; @@ -173,23 +198,23 @@ public class GoodsMessages extends BaseEntity { return modified; } - public void setGoodsType(String goodsType) + public void setGoodsName(String goodsName) { - this.goodsType = goodsType; + this.goodsName = goodsName; } - public String getGoodsType() + public String getGoodsName() { - return goodsType; + return goodsName; } - public void setGoodsSpec(String goodsSpec) + public void setGoodsModel(String goodsModel) { - this.goodsSpec = goodsSpec; + this.goodsModel = goodsModel; } - public String getGoodsSpec() + public String getGoodsModel() { - return goodsSpec; + return goodsModel; } @Override @@ -203,14 +228,16 @@ public class GoodsMessages extends BaseEntity .append("productPicture", getProductPicture()) .append("originalPrice", getOriginalPrice()) .append("productDesc", getProductDesc()) + .append("goodsType", getGoodsType()) + .append("goodsSpec", getGoodsSpec()) .append("orderNum", getOrderNum()) .append("isDelete", getIsDelete()) .append("created", getCreated()) .append("modified", getModified()) .append("createTime", getCreateTime()) .append("updateTime", getUpdateTime()) - .append("goodsType", getGoodsType()) - .append("goodsSpec", getGoodsSpec()) + .append("goodsName", getGoodsName()) + .append("goodsModel", getGoodsModel()) .toString(); } } diff --git a/sf-admin/src/main/java/com/sf/goods/mapper/GoodsMessagesMapper.java b/sf-service/src/main/java/com/sf/service/mapper/GoodsMessagesMapper.java similarity index 87% rename from sf-admin/src/main/java/com/sf/goods/mapper/GoodsMessagesMapper.java rename to sf-service/src/main/java/com/sf/service/mapper/GoodsMessagesMapper.java index dfb2188..02a2f1a 100644 --- a/sf-admin/src/main/java/com/sf/goods/mapper/GoodsMessagesMapper.java +++ b/sf-service/src/main/java/com/sf/service/mapper/GoodsMessagesMapper.java @@ -1,13 +1,13 @@ -package com.sf.goods.mapper; +package com.sf.service.mapper; import java.util.List; -import com.sf.goods.domain.GoodsMessages; +import com.sf.service.domain.GoodsMessages; /** * 商品信息Mapper接口 * - * @author ztzh - * @date 2024-04-11 + * @author zoukun + * @date 2024-04-18 */ public interface GoodsMessagesMapper { @@ -58,4 +58,6 @@ public interface GoodsMessagesMapper * @return 结果 */ public int deleteGoodsMessagesByIds(Long[] ids); + + GoodsMessages selectGoodsMessagesByCode(String goodsCode); } diff --git a/sf-admin/src/main/java/com/sf/goods/service/IGoodsMessagesService.java b/sf-service/src/main/java/com/sf/service/service/IGoodsMessagesService.java similarity index 87% rename from sf-admin/src/main/java/com/sf/goods/service/IGoodsMessagesService.java rename to sf-service/src/main/java/com/sf/service/service/IGoodsMessagesService.java index 3f1c08a..29f7dab 100644 --- a/sf-admin/src/main/java/com/sf/goods/service/IGoodsMessagesService.java +++ b/sf-service/src/main/java/com/sf/service/service/IGoodsMessagesService.java @@ -1,13 +1,13 @@ -package com.sf.goods.service; +package com.sf.service.service; import java.util.List; -import com.sf.goods.domain.GoodsMessages; +import com.sf.service.domain.GoodsMessages; /** * 商品信息Service接口 * - * @author ztzh - * @date 2024-04-11 + * @author zoukun + * @date 2024-04-18 */ public interface IGoodsMessagesService { @@ -58,4 +58,6 @@ public interface IGoodsMessagesService * @return 结果 */ public int deleteGoodsMessagesById(Long id); + + GoodsMessages selectGoodsMessagesByCode(String goodsCode); } diff --git a/sf-admin/src/main/java/com/sf/goods/service/impl/GoodsMessagesServiceImpl.java b/sf-service/src/main/java/com/sf/service/service/impl/GoodsMessagesServiceImpl.java similarity index 80% rename from sf-admin/src/main/java/com/sf/goods/service/impl/GoodsMessagesServiceImpl.java rename to sf-service/src/main/java/com/sf/service/service/impl/GoodsMessagesServiceImpl.java index 45b7b42..0541ba8 100644 --- a/sf-admin/src/main/java/com/sf/goods/service/impl/GoodsMessagesServiceImpl.java +++ b/sf-service/src/main/java/com/sf/service/service/impl/GoodsMessagesServiceImpl.java @@ -1,18 +1,19 @@ -package com.sf.goods.service.impl; +package com.sf.service.service.impl; import java.util.List; import com.sf.common.utils.DateUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.sf.goods.mapper.GoodsMessagesMapper; -import com.sf.goods.domain.GoodsMessages; -import com.sf.goods.service.IGoodsMessagesService; +import com.sf.service.mapper.GoodsMessagesMapper; +import com.sf.service.domain.GoodsMessages; +import com.sf.service.service.IGoodsMessagesService; +import org.springframework.util.StringUtils; /** * 商品信息Service业务层处理 * - * @author ztzh - * @date 2024-04-11 + * @author zoukun + * @date 2024-04-18 */ @Service public class GoodsMessagesServiceImpl implements IGoodsMessagesService @@ -93,4 +94,12 @@ public class GoodsMessagesServiceImpl implements IGoodsMessagesService { return goodsMessagesMapper.deleteGoodsMessagesById(id); } + + @Override + public GoodsMessages selectGoodsMessagesByCode(String goodsCode) { + if (StringUtils.hasText(goodsCode)){ + return goodsMessagesMapper.selectGoodsMessagesByCode(goodsCode); + } + return null; + } } diff --git a/sf-admin/src/main/resources/mapper/goods/GoodsMessagesMapper.xml b/sf-service/src/main/resources/mapper/service/GoodsMessagesMapper.xml similarity index 76% rename from sf-admin/src/main/resources/mapper/goods/GoodsMessagesMapper.xml rename to sf-service/src/main/resources/mapper/service/GoodsMessagesMapper.xml index ffa20fa..3e55f20 100644 --- a/sf-admin/src/main/resources/mapper/goods/GoodsMessagesMapper.xml +++ b/sf-service/src/main/resources/mapper/service/GoodsMessagesMapper.xml @@ -1,118 +1,130 @@ - - - - - - - - - - - - - - - - - - - - - - - - - select id, goods_code, stock_id, review_status, product_title, product_picture, original_price, product_desc, order_num, is_delete, created, modified, create_time, update_time, goods_type, goods_spec from GOODS_MESSAGES - - - - - - - - - insert into GOODS_MESSAGES - - goods_code, - stock_id, - review_status, - product_title, - product_picture, - original_price, - product_desc, - order_num, - is_delete, - created, - modified, - create_time, - update_time, - goods_type, - goods_spec, - - - #{goodsCode}, - #{stockId}, - #{reviewStatus}, - #{productTitle}, - #{productPicture}, - #{originalPrice}, - #{productDesc}, - #{orderNum}, - #{isDelete}, - #{created}, - #{modified}, - #{createTime}, - #{updateTime}, - #{goodsType}, - #{goodsSpec}, - - - - - update GOODS_MESSAGES - - goods_code = #{goodsCode}, - stock_id = #{stockId}, - review_status = #{reviewStatus}, - product_title = #{productTitle}, - product_picture = #{productPicture}, - original_price = #{originalPrice}, - product_desc = #{productDesc}, - order_num = #{orderNum}, - is_delete = #{isDelete}, - created = #{created}, - modified = #{modified}, - create_time = #{createTime}, - update_time = #{updateTime}, - goods_type = #{goodsType}, - goods_spec = #{goodsSpec}, - - where id = #{id} - - - - delete from GOODS_MESSAGES where id = #{id} - - - - delete from GOODS_MESSAGES where id in - - #{id} - - + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, goods_code, stock_id, review_status, product_title, product_picture, original_price, product_desc, goods_type, goods_spec, order_num, is_delete, created, modified, create_time, update_time, goods_name, goods_model from GOODS_MESSAGES + + + + + + + + + insert into GOODS_MESSAGES + + goods_code, + stock_id, + review_status, + product_title, + product_picture, + original_price, + product_desc, + goods_type, + goods_spec, + order_num, + is_delete, + created, + modified, + create_time, + update_time, + goods_name, + goods_model, + + + #{goodsCode}, + #{stockId}, + #{reviewStatus}, + #{productTitle}, + #{productPicture}, + #{originalPrice}, + #{productDesc}, + #{goodsType}, + #{goodsSpec}, + #{orderNum}, + #{isDelete}, + #{created}, + #{modified}, + #{createTime}, + #{updateTime}, + #{goodsName}, + #{goodsModel}, + + + + + update GOODS_MESSAGES + + goods_code = #{goodsCode}, + stock_id = #{stockId}, + review_status = #{reviewStatus}, + product_title = #{productTitle}, + product_picture = #{productPicture}, + original_price = #{originalPrice}, + product_desc = #{productDesc}, + goods_type = #{goodsType}, + goods_spec = #{goodsSpec}, + order_num = #{orderNum}, + is_delete = #{isDelete}, + created = #{created}, + modified = #{modified}, + create_time = #{createTime}, + update_time = #{updateTime}, + goods_name = #{goodsName}, + goods_model = #{goodsModel}, + + where id = #{id} + + + + delete from GOODS_MESSAGES where id = #{id} + + + + delete from GOODS_MESSAGES where id in + + #{id} + + \ No newline at end of file diff --git a/sf-ui/src/api/goods/goods.js b/sf-ui/src/api/service/goods.js similarity index 76% rename from sf-ui/src/api/goods/goods.js rename to sf-ui/src/api/service/goods.js index 68f58bc..ce3cca2 100644 --- a/sf-ui/src/api/goods/goods.js +++ b/sf-ui/src/api/service/goods.js @@ -1,44 +1,44 @@ -import request from '@/utils/request' - -// 查询商品信息列表 -export function listGoods(query) { - return request({ - url: '/goods/goods/list', - method: 'get', - params: query - }) -} - -// 查询商品信息详细 -export function getGoods(id) { - return request({ - url: '/goods/goods/' + id, - method: 'get' - }) -} - -// 新增商品信息 -export function addGoods(data) { - return request({ - url: '/goods/goods', - method: 'post', - data: data - }) -} - -// 修改商品信息 -export function updateGoods(data) { - return request({ - url: '/goods/goods', - method: 'put', - data: data - }) -} - -// 删除商品信息 -export function delGoods(id) { - return request({ - url: '/goods/goods/' + id, - method: 'delete' - }) -} +import request from '@/utils/request' + +// 查询商品信息列表 +export function listGoods(query) { + return request({ + url: '/service/goods/list', + method: 'get', + params: query + }) +} + +// 查询商品信息详细 +export function getGoods(id) { + return request({ + url: '/service/goods/' + id, + method: 'get' + }) +} + +// 新增商品信息 +export function addGoods(data) { + return request({ + url: '/service/goods', + method: 'post', + data: data + }) +} + +// 修改商品信息 +export function updateGoods(data) { + return request({ + url: '/service/goods', + method: 'put', + data: data + }) +} + +// 删除商品信息 +export function delGoods(id) { + return request({ + url: '/service/goods/' + id, + method: 'delete' + }) +} diff --git a/sf-ui/src/views/goods/goods/index.vue b/sf-ui/src/views/service/goods/index.vue similarity index 67% rename from sf-ui/src/views/goods/goods/index.vue rename to sf-ui/src/views/service/goods/index.vue index 819c794..c8bf789 100644 --- a/sf-ui/src/views/goods/goods/index.vue +++ b/sf-ui/src/views/service/goods/index.vue @@ -1,348 +1,423 @@ - - - + + + From 470c0a000c668b6cde45bcd5606070afe906cdbe Mon Sep 17 00:00:00 2001 From: akun <957746831@qq.com> Date: Thu, 18 Apr 2024 15:59:14 +0800 Subject: [PATCH 10/16] =?UTF-8?q?=E4=BC=9A=E5=91=98=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sf/web/controller/system/UserMemberController.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sf-admin/src/main/java/com/sf/web/controller/system/UserMemberController.java b/sf-admin/src/main/java/com/sf/web/controller/system/UserMemberController.java index 8454cf7..294c62a 100644 --- a/sf-admin/src/main/java/com/sf/web/controller/system/UserMemberController.java +++ b/sf-admin/src/main/java/com/sf/web/controller/system/UserMemberController.java @@ -2,6 +2,8 @@ package com.sf.web.controller.system; import java.util.List; import javax.servlet.http.HttpServletResponse; + +import com.sf.common.utils.SecurityUtils; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; @@ -71,10 +73,12 @@ public class UserMemberController extends BaseController /** * 获取会员详细信息,通过用户id + * 当前用户 */ - @GetMapping(value = "/byUser") - public AjaxResult getInfoByUser(Long userId) + @GetMapping(value = "/currentUser") + public AjaxResult getInfoByUser() { + Long userId = SecurityUtils.getUserId(); return success(userMemberService.selectUserMemberByUserId(userId)); } From 4d1e3f6ad81b42d14f9649c3dc1e657fae7047df Mon Sep 17 00:00:00 2001 From: akun <957746831@qq.com> Date: Thu, 18 Apr 2024 16:07:10 +0800 Subject: [PATCH 11/16] =?UTF-8?q?=E4=BC=9A=E5=91=98=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf-admin/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sf-admin/src/main/resources/application.yml b/sf-admin/src/main/resources/application.yml index d953204..843f1c4 100644 --- a/sf-admin/src/main/resources/application.yml +++ b/sf-admin/src/main/resources/application.yml @@ -18,7 +18,7 @@ sf: # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 - port: 80 + port: 7781 servlet: # 应用的访问路径 context-path: / From 0b698e8fc26b477ec6672e3f8dad8c4f45b74871 Mon Sep 17 00:00:00 2001 From: akun <957746831@qq.com> Date: Thu, 18 Apr 2024 16:18:45 +0800 Subject: [PATCH 12/16] =?UTF-8?q?pom=E7=BB=93=E6=9E=84=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 9c6a7a9..cb3a298 100644 --- a/pom.xml +++ b/pom.xml @@ -225,6 +225,9 @@ sf-common sf-apijson sf-oauth + sf-payment + sf-service + sf-order pom From 30ad0af85b504d6606e92f5b475133416e11bde6 Mon Sep 17 00:00:00 2001 From: akun <957746831@qq.com> Date: Thu, 18 Apr 2024 16:43:46 +0800 Subject: [PATCH 13/16] =?UTF-8?q?=E5=95=86=E5=93=81=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=9D=83=E9=99=90=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/sf/service/controller/GoodsMessagesController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java b/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java index 4117dab..ad4abd8 100644 --- a/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java +++ b/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java @@ -38,7 +38,6 @@ public class GoodsMessagesController extends BaseController /** * 查询商品信息列表 */ - @PreAuthorize("@ss.hasPermi('service:goods:list')") @GetMapping("/list") public TableDataInfo list(GoodsMessages goodsMessages) { From 72d51a2fbfcd2928be8a5cd5d2570877827f2922 Mon Sep 17 00:00:00 2001 From: akun <957746831@qq.com> Date: Thu, 18 Apr 2024 17:01:43 +0800 Subject: [PATCH 14/16] =?UTF-8?q?=E5=95=86=E5=93=81=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf-ui/src/views/service/goods/index.vue | 54 +++++++++---------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/sf-ui/src/views/service/goods/index.vue b/sf-ui/src/views/service/goods/index.vue index c8bf789..e90d841 100644 --- a/sf-ui/src/views/service/goods/index.vue +++ b/sf-ui/src/views/service/goods/index.vue @@ -9,8 +9,8 @@ @keyup.enter.native="handleQuery" /> - - + + - - + + - + @@ -130,7 +130,7 @@ - + @@ -156,7 +156,7 @@ - + - - - - + - + - - - - - - - - - - - - @@ -279,6 +264,9 @@ export default { goodsCode: [ { required: true, message: "商品编号不能为空", trigger: "blur" } ], + reviewStatus: [ + { required: true, message: "审核状态不能为空", trigger: "change" } + ], productTitle: [ { required: true, message: "商品标题不能为空", trigger: "blur" } ], @@ -289,20 +277,14 @@ export default { { required: true, message: "商品原价不能为空", trigger: "blur" } ], goodsType: [ - { required: true, message: "商品类型。 * • 0:消耗型商品 * • 1:非消耗型商品 * • 2:自动续期订阅商品不能为空", trigger: "change" } + { required: true, message: "商品类型不能为空", trigger: "change" } ], goodsSpec: [ { required: true, message: "商品规格不能为空", trigger: "blur" } ], - orderNum: [ - { required: true, message: "排序不能为空", trigger: "blur" } - ], - createTime: [ - { required: true, message: "创建时间不能为空", trigger: "blur" } - ], - goodsName: [ - { required: true, message: "商品名称不能为空", trigger: "blur" } - ], + goodsModel: [ + { required: true, message: "商品型号不能为空", trigger: "blur" } + ] } }; }, @@ -385,8 +367,8 @@ export default { this.$refs["form"].validate(valid => { if (valid) { if (this.form.id != null) { - let data = Object.assign({},this.form) - delete data.explain + let data = Object.assign({},this.form) + delete data.explain updateGoods(data).then(response => { this.$modal.msgSuccess("修改成功"); this.open = false; From 7730dfa2b1ffb7e14d29db565ca0f25060bb4715 Mon Sep 17 00:00:00 2001 From: pengren Date: Mon, 15 Apr 2024 11:52:31 +0800 Subject: [PATCH 15/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E5=AE=89=E8=A3=85=E5=8C=85=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/common/CommonController.java | 1 - sf-admin/src/main/resources/file/server.crt | 20 + sf-common/pom.xml | 6 + .../com/sf/common/utils/file/FileUtils.java | 33 +- .../com/sf/common/utils/file/ZipUtils.java | 137 ++++++ .../sf/common/utils/openssl/Base64Utils.java | 112 +++++ .../com/sf/common/utils/openssl/ByteUtil.java | 72 +++ .../sf/common/utils/openssl/RsaSignUtil.java | 447 ++++++++++++++++++ .../com/sf/common/utils/sign/Sha265Utils.java | 26 + .../sf/file/controller/SysOssController.java | 17 +- .../com/sf/file/service/ISysOssService.java | 4 + .../file/service/impl/SysOssServiceImpl.java | 9 +- 12 files changed, 875 insertions(+), 9 deletions(-) create mode 100644 sf-admin/src/main/resources/file/server.crt create mode 100644 sf-common/src/main/java/com/sf/common/utils/file/ZipUtils.java create mode 100644 sf-common/src/main/java/com/sf/common/utils/openssl/Base64Utils.java create mode 100644 sf-common/src/main/java/com/sf/common/utils/openssl/ByteUtil.java create mode 100644 sf-common/src/main/java/com/sf/common/utils/openssl/RsaSignUtil.java create mode 100644 sf-common/src/main/java/com/sf/common/utils/sign/Sha265Utils.java diff --git a/sf-admin/src/main/java/com/sf/web/controller/common/CommonController.java b/sf-admin/src/main/java/com/sf/web/controller/common/CommonController.java index 8bd4c5a..ab7e914 100644 --- a/sf-admin/src/main/java/com/sf/web/controller/common/CommonController.java +++ b/sf-admin/src/main/java/com/sf/web/controller/common/CommonController.java @@ -96,7 +96,6 @@ public class CommonController return AjaxResult.error(e.getMessage()); } } - /** * 通用上传请求(多个) */ diff --git a/sf-admin/src/main/resources/file/server.crt b/sf-admin/src/main/resources/file/server.crt new file mode 100644 index 0000000..297fd5c --- /dev/null +++ b/sf-admin/src/main/resources/file/server.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSDCCAjACCQCvqL+z6dKTrjANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJD +TjERMA8GA1UECAwIc2hpY2h1YW4xEDAOBgNVBAcMB2NoZW5nZHUxEjAQBgNVBAoM +CXpob25ndGlhbjEKMAgGA1UECwwBQjESMBAGA1UEAwwJemhvbmd0aWFuMB4XDTI0 +MDQxNzA2MjIxNloXDTI1MDQxNzA2MjIxNlowZjELMAkGA1UEBhMCQ04xETAPBgNV +BAgMCHNoaWNodWFuMRAwDgYDVQQHDAdjaGVuZ2R1MRIwEAYDVQQKDAl6aG9uZ3Rp +YW4xCjAIBgNVBAsMAUIxEjAQBgNVBAMMCXpob25ndGlhbjCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAMIXQc0iVEuN/c8PnryNQYP3WbF6irqnjTbk/kCV +552p30ZmdeRDGcFKdkohQLKT06sbqlaBzhGFpnLlb5iz1dG1sGZbw064RFZRlgED +P7ROP891dvj9scPxfWC1Moy6/9LXBQLIshD7zTxzOemqxdyJ1jELianrWWm4XFKM +jvl7BGcfS/VIEafrJ5WGzuRMKtf6SY8aD5Olu0sN61tnSIgjWwZnKkv4V764UBpN +ybTsk1ctefUNMkE5Q+b2gu+0i5qH18B5/H5O7oAEAA+IWIPxIzuINnAYvHRhNQ/W +LMaI/GhYpQMhlmXtOmFFFe3fL4fKRMCKa3x2Va/GzfPjwecCAwEAATANBgkqhkiG +9w0BAQsFAAOCAQEAZmqK3ZfCA3A99VRfSRmCnpBbAW7SIxNeRqKFa5dunOMb7R6B ++bV/ALLJmu8S5D7pSR/wqCGURmnetUnBAEQmYJsjSq3142PeADkr8NmZ9bOXBVNM +szfSPOeM8HCUVjG9r+VMuy6yuPXFzp6QcKC13pzdziMdTHyKSu25fbYwE3lYOXk9 +4nLpnJeT09gecXPdGllrVTKkgkE7L7h4iX/QzsGiZ/JbrCD77nPFqr7fkepsrHjl +4U1Pfjc3NStquhT5ZINDr7MBg/JaHNW57ynt1ud0y1lUOu2o5fpmyYVy/4llRl+T +VJ6mJYXIWr+i10bVjNv2tGPEylmI1JHelYRqAQ== +-----END CERTIFICATE----- diff --git a/sf-common/pom.xml b/sf-common/pom.xml index b6c4893..a370090 100644 --- a/sf-common/pom.xml +++ b/sf-common/pom.xml @@ -15,6 +15,12 @@ + + + commons-fileupload + commons-fileupload + 1.4 + org.springframework diff --git a/sf-common/src/main/java/com/sf/common/utils/file/FileUtils.java b/sf-common/src/main/java/com/sf/common/utils/file/FileUtils.java index c53b282..691c88b 100644 --- a/sf-common/src/main/java/com/sf/common/utils/file/FileUtils.java +++ b/sf-common/src/main/java/com/sf/common/utils/file/FileUtils.java @@ -6,25 +6,27 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import com.sf.common.config.SmarterFrameworkConfig; -import com.sf.common.exception.ServiceException; import com.sf.common.utils.DateUtils; import com.sf.common.utils.StringUtils; import com.sf.common.utils.uuid.IdUtils; import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.commons.CommonsMultipartFile; /** * 文件处理工具类 @@ -295,5 +297,28 @@ public class FileUtils String baseName = FilenameUtils.getBaseName(fileName); return baseName; } + /** + * 根据OutputStream,生成文件 + */ + public static MultipartFile outputToFile(ByteArrayOutputStream bos) throws IOException { + try { + // 文件输出流 + byte[] bytes = bos.toByteArray(); + + FileItemFactory factory = new DiskFileItemFactory(16, null); + FileItem item = factory.createItem("file", "application/zip", false, "apk.zip"); + OutputStream os = item.getOutputStream(); + os.write(bytes); + bos.flush(); + os.flush(); + + // MultipartFile + MultipartFile file = new CommonsMultipartFile(item); + return file; + } catch (Exception e) { + System.err.println("加载私钥失败"); + } + return null; + } } diff --git a/sf-common/src/main/java/com/sf/common/utils/file/ZipUtils.java b/sf-common/src/main/java/com/sf/common/utils/file/ZipUtils.java new file mode 100644 index 0000000..d3afc39 --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/file/ZipUtils.java @@ -0,0 +1,137 @@ +package com.sf.common.utils.file; + + + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + + +/** + * 压缩文件工具类 + */ +public class ZipUtils { + private ZipFile zipFile; + private ZipOutputStream zipOut; //压缩Zip + private ZipEntry zipEntry; + private static int bufSize; //size of bytes + private byte[] buf; + private int readedBytes; + + public ZipUtils() { + this(512); + } + + public ZipUtils(int bufSize) { + this.bufSize = bufSize; + this.buf = new byte[this.bufSize]; + } + + //压缩文件夹内的文件 + public void doZip(String zipDirectory) {//zipDirectoryPath:需要压缩的文件夹名 + File file; + File zipDir; + + zipDir = new File(zipDirectory); + String zipFileName = zipDir.getName() + ".zip";//压缩后生成的zip文件名 + + try { + this.zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFileName))); + handleDir(zipDir, this.zipOut); + this.zipOut.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + //由doZip调用,递归完成目录文件读取 + public void handleDir(File dir, ZipOutputStream zipOut) throws IOException { + FileInputStream fileIn; + File[] files; + + files = dir.listFiles(); + + if (files.length == 0) {//如果目录为空,则单独创建之. + //ZipEntry的isDirectory()方法中,目录以"/"结尾. + this.zipOut.putNextEntry(new ZipEntry(dir.toString() + "/")); + this.zipOut.closeEntry(); + } else {//如果目录不为空,则分别处理目录和文件. + for (File fileName : files) { + //System.out.println(fileName); + + if (fileName.isDirectory()) { + handleDir(fileName, this.zipOut); + } else { + fileIn = new FileInputStream(fileName); + this.zipOut.putNextEntry(new ZipEntry(fileName.toString())); + + while ((this.readedBytes = fileIn.read(this.buf)) > 0) { + this.zipOut.write(this.buf, 0, this.readedBytes); + } + } + } + } + } + + public static void zip(ZipOutputStream zipOut, InputStream inputStream, String fileName) throws IOException { + zipOut.putNextEntry(new ZipEntry(fileName)); + + int readBytes = 512; + byte[] buf = new byte[readBytes]; + while ((readBytes = inputStream.read(buf)) > 0) { + zipOut.write(buf, 0, readBytes); + } + zipOut.closeEntry(); + } + + //解压指定zip文件 + public void unZip(String unZipfileName) {//unZipfileName需要解压的zip文件名 + FileOutputStream fileOut; + File file; + InputStream inputStream; + + try { + this.zipFile = new ZipFile(unZipfileName); + + for (Enumeration entries = this.zipFile.entries(); entries.hasMoreElements(); ) { + ZipEntry entry = (ZipEntry) entries.nextElement(); + file = new File(entry.getName()); + + if (entry.isDirectory()) { + file.mkdirs(); + } else { + //如果指定文件的目录不存在,则创建之. + File parent = file.getParentFile(); + if (!parent.exists()) { + parent.mkdirs(); + } + + inputStream = zipFile.getInputStream(entry); + + fileOut = new FileOutputStream(file); + while ((this.readedBytes = inputStream.read(this.buf)) > 0) { + fileOut.write(this.buf, 0, this.readedBytes); + } + fileOut.close(); + + inputStream.close(); + } + } + this.zipFile.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + //设置缓冲区大小 + public void setBufSize(int bufSize) { + this.bufSize = bufSize; + } +} \ No newline at end of file diff --git a/sf-common/src/main/java/com/sf/common/utils/openssl/Base64Utils.java b/sf-common/src/main/java/com/sf/common/utils/openssl/Base64Utils.java new file mode 100644 index 0000000..ed28f3b --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/openssl/Base64Utils.java @@ -0,0 +1,112 @@ +package com.sf.common.utils.openssl; + +import java.io.UnsupportedEncodingException; + +/** + * Base64 加密解密工具类 + * @author XIHONGLEI + * @date 2018-03-27 + */ +public class Base64Utils { + private static char[] base64EncodeChars = new char[] + {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '+', '/'}; + private static byte[] base64DecodeChars = new byte[] + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, + -1, -1, -1}; + + public static String encode(byte[] data) { + StringBuffer sb = new StringBuffer(); + int len = data.length; + int i = 0; + int b1, b2, b3; + while (i < len) { + b1 = data[i++] & 0xff; + if (i == len) { + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[(b1 & 0x3) << 4]); + sb.append("=="); + break; + } + b2 = data[i++] & 0xff; + if (i == len) { + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); + sb.append(base64EncodeChars[(b2 & 0x0f) << 2]); + sb.append("="); + break; + } + b3 = data[i++] & 0xff; + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); + sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]); + sb.append(base64EncodeChars[b3 & 0x3f]); + } + return sb.toString(); + } + + public static byte[] decode(String str) { + try { + return decodePrivate(str); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return new byte[] + {}; + } + + private static byte[] decodePrivate(String str) throws UnsupportedEncodingException { + StringBuffer sb = new StringBuffer(); + byte[] data = null; + data = str.getBytes("US-ASCII"); + int len = data.length; + int i = 0; + int b1, b2, b3, b4; + while (i < len) { + do { + b1 = base64DecodeChars[data[i++]]; + } while (i < len && b1 == -1); + if (b1 == -1) { + break; + + } + do { + b2 = base64DecodeChars[data[i++]]; + } while (i < len && b2 == -1); + if (b2 == -1) { + break; + } + sb.append((char) ((b1 << 2) | ((b2 & 0x30) >>> 4))); + + do { + b3 = data[i++]; + if (b3 == 61) { + return sb.toString().getBytes("iso8859-1"); + } + b3 = base64DecodeChars[b3]; + } while (i < len && b3 == -1); + if (b3 == -1) { + break; + } + sb.append((char) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2))); + do { + b4 = data[i++]; + if (b4 == 61) { + return sb.toString().getBytes("iso8859-1"); + } + b4 = base64DecodeChars[b4]; + } while (i < len && b4 == -1); + if (b4 == -1) { + break; + } + sb.append((char) (((b3 & 0x03) << 6) | b4)); + } + return sb.toString().getBytes("iso8859-1"); + } +} \ No newline at end of file diff --git a/sf-common/src/main/java/com/sf/common/utils/openssl/ByteUtil.java b/sf-common/src/main/java/com/sf/common/utils/openssl/ByteUtil.java new file mode 100644 index 0000000..21eb69d --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/openssl/ByteUtil.java @@ -0,0 +1,72 @@ +package com.sf.common.utils.openssl; + +import java.io.*; + +/** + * byte数组工具类实现byte[]与文件之间的相互转换 + * @author XIHONGLEI + * @Date 2018-03-26 + */ +public class ByteUtil { + /** + * 获得指定文件的byte数组 + */ + public static byte[] getBytes(String filePath){ + byte[] buffer = null; + try { + File file = new File(filePath); + FileInputStream fis = new FileInputStream(file); + ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); + byte[] b = new byte[1000]; + int n; + while ((n = fis.read(b)) != -1) { + bos.write(b, 0, n); + } + fis.close(); + bos.close(); + buffer = bos.toByteArray(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return buffer; + } + + /** + * 根据byte数组,生成文件 + */ + public static void saveFile(byte[] bfile, String filePath,String fileName) { + BufferedOutputStream bos = null; + FileOutputStream fos = null; + File file = null; + try { + File dir = new File(filePath); + //判断文件目录是否存在 + if(!dir.exists()&&dir.isDirectory()){ + dir.mkdirs(); + } + file = new File(filePath+"\\"+fileName); + fos = new FileOutputStream(file); + bos = new BufferedOutputStream(fos); + bos.write(bfile); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (bos != null) { + try { + bos.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + if (fos != null) { + try { + fos.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + } +} \ No newline at end of file diff --git a/sf-common/src/main/java/com/sf/common/utils/openssl/RsaSignUtil.java b/sf-common/src/main/java/com/sf/common/utils/openssl/RsaSignUtil.java new file mode 100644 index 0000000..ed5026e --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/openssl/RsaSignUtil.java @@ -0,0 +1,447 @@ +package com.sf.common.utils.openssl; + +import cn.hutool.core.io.resource.ClassPathResource; +import com.sf.common.utils.file.FileUtils; +import com.sf.common.utils.file.ZipUtils; +import com.sf.common.utils.sign.Sha265Utils; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.*; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.zip.ZipOutputStream; + +/** + * rsa加密 + */ +@Component +public class RsaSignUtil { + + private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqvm+B2e3shKKN1PGXfTWGc7+gC+XnKvtZX/T9ADWtL7KvAqhAREtxcFRtPC9ONbEqSfHb53mUUJbmKrp5C1rFrWQahBP8mpFOtjoReOkqF/idbwAKtvOq5DIuQT7Wl7wgDdh4OvEWqnUaWvkV0qJNK/Cny1YL8R+DJrnlAiREQwIDAQAB"; + private static final String PRIVATE_KEY = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDCF0HNIlRLjf3PD568jUGD91mxeoq6p4025P5Aleedqd9GZnXkQxnBSnZKIUCyk9OrG6pWgc4RhaZy5W+Ys9XRtbBmW8NOuERWUZYBAz+0Tj/PdXb4/bHD8X1gtTKMuv/S1wUCyLIQ+808cznpqsXcidYxC4mp61lpuFxSjI75ewRnH0v1SBGn6yeVhs7kTCrX+kmPGg+TpbtLDetbZ0iII1sGZypL+Fe+uFAaTcm07JNXLXn1DTJBOUPm9oLvtIuah9fAefx+Tu6ABAAPiFiD8SM7iDZwGLx0YTUP1izGiPxoWKUDIZZl7TphRRXt3y+HykTAimt8dlWvxs3z48HnAgMBAAECggEAMnoB7uuCqLOg0nzpd3f1FfD6LqID+k8g9qnPRdS2i9Zl7tUuy9J3nEFVSiojvU2cut9TCXRwtzXe9/qbb0c10HhEDBrJTGu3ugs1y8Wgh6/iAH44FsFDvgXL6Z62KstnRNoOv46aYLhbw+DjqtVy58whHFg30S0X79q2h/FuLYtQgju0I5yDMBVmbTmJKvhKtcATBUKnXxsAIMnc/BWXHNV9dg/Yfx1JDOqZqxFUsYmnMz5CatfGqnyc7AH1S0SFp4hIYsA1iPuEYDtFM8AAyP0PB4Z+buaG12xMF4S35OqDP0uhYpsh8/vQ28xqn0hAGsA1lAb792nZ5Qf/0WQGgQKBgQDuTNJdyIQN9Oa/DrQwDEPpyiurGBqY6ZvJHq1CpDy4AbMkIEYlVBnFPHCjyiWyW61MT80qMXyCQn3DLs5MsbB3rNZYItD9Tqmd5vkxD3B4zRneH1FQUuWR64lqM0fjFiy7+gfpn3tC5169ynn+u1oArwEnA577c93GHmr/P+I5FwKBgQDQgdCW8wTrLYLWrG7pg/VshWJX+J/8vGM8b2WPF52hIX7HhIouQjiwhAcMam0vwg0ZiPJH0Rw67PWrPxz1GOtfBwSXD74j/0QGdo/NwN8HxcXoGRFCb44KbDKj7r9xIwQ7eir93S7wb6FWzfzM4hcgQy6lZQO1wA1ld/n8GkOfsQKBgQDSZJUfJBgjUmQfr1W7TjjvwwWHC8L/2tEmYqVKP0M0mpuVe8Ey1+vgzhoNIj7iGDsLMzHK3OI3B4mNuFeAMynAn6KnMiYeX0M2HGVKwXzlyv/ZswDR0D7L9I7gauI/Rqa8WGaAB8SGL0zgeFH5a2RtUhXMYlANZiEZ+GUzcy3ChQKBgQCRtDV0Vox4X4xPwZ2b9we49xi7CtWaAneCUwHwVXtkjwiOGhOJseL1jr1yMqjsN7l5v5Y3E2ybzNz79xWgPPK1rCzL90dS1l3N+moRSRdR0iWzRFkoSNZEeEmG/x6uy954Ra3nSvozMH7ZLwbakZ/BftEI6ScHihaScp2xSxU4cQKBgQDANT+6d3E6aGT2oAdTxAdimcLEWZOVW9gkbX/UASEpTjEMDdr+MoPuKSPYxuMcWf/HksJF8rAVYJjklcIZHI1TF0Mm7RVrM+Kw3VzHR21jf7RNboBAAH+YeUH9yZg936iqO4Gi5wegUtttXgrxxG5rwi8iUXbbiC/xLVm0ldHW5Q=="; + public static MultipartFile signZip(MultipartFile file) throws Exception { + RsaSignUtil rsaSignUtil = new RsaSignUtil(); + //初始化密钥 + try { + rsaSignUtil.loadPrivateKey(PRIVATE_KEY); + System.out.println("加载私钥成功"); + } catch (Exception e) { + System.err.println(e.getMessage()); + System.err.println("加载私钥失败"); + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream out = new ZipOutputStream(outputStream); + //放入文件 + ZipUtils.zip(out, new ByteArrayInputStream(file.getBytes()), file.getOriginalFilename()); + //放入签名 + String encrypt = Sha265Utils.encrypt(file.getBytes()); + ZipUtils.zip(out, new ByteArrayInputStream(rsaSignUtil.rsaSign(encrypt.getBytes(), rsaSignUtil.getPrivateKey())), "sign.signture"); + //放入证书 + ZipUtils.zip(out, new ByteArrayInputStream(new ClassPathResource("file/server.crt").readBytes()), "server.crt"); + outputStream.close(); + out.close(); + return FileUtils.outputToFile(outputStream); + } + /** + * rsa签名 + * @param data 待签名的字符串 + * @param priKey rsa私钥字符串 + * @return 签名结果 + * @throws Exception 签名失败则抛出异常 + */ + public byte[] rsaSign(byte[] data, RSAPrivateKey priKey) throws SignatureException { + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(priKey); + signature.update(data); + + byte[] signed = signature.sign(); + return signed; + } catch (Exception e) { + throw new SignatureException("RSAcontent = " + data + + "; charset = ", e); + } + } + + /** + * rsa验签 + * + * @param data 被签名的内容 + * @param sign 签名后的结果 + * @param pubKey rsa公钥 + * @return 验签结果 + * @throws SignatureException 验签失败,则抛异常 + */ + public boolean verify(byte[] data, byte[] sign, RSAPublicKey pubKey) + throws SignatureException { + try { + Signature signature = Signature.getInstance("SHA512withRSA"); + signature.initVerify(pubKey); + signature.update(data); + return signature.verify(sign); + + } catch (Exception e) { + e.printStackTrace(); + throw new SignatureException("RSA验证签名[content = " + data + + "; charset = " + "; signature = " + sign + "]发生异常!", e); + } + } + + /** + * 私钥 + */ + private RSAPrivateKey privateKey; + + /** + * 公钥 + */ + private RSAPublicKey publicKey; + + /** + * 字节数据转字符串专用集合 + */ + private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 获取私钥 + * + * @return 当前的私钥对象 + */ + public RSAPrivateKey getPrivateKey() { + return privateKey; + } + + /** + * 获取公钥 + * + * @return 当前的公钥对象 + */ + public RSAPublicKey getPublicKey() { + return publicKey; + } + + /** + * 随机生成密钥对 + */ + public void genKeyPair() { + KeyPairGenerator keyPairGen = null; + try { + keyPairGen = KeyPairGenerator.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + keyPairGen.initialize(1024, new SecureRandom()); + KeyPair keyPair = keyPairGen.generateKeyPair(); + this.privateKey = (RSAPrivateKey) keyPair.getPrivate(); + this.publicKey = (RSAPublicKey) keyPair.getPublic(); + } + + /** + * 从.pem文件中取得私钥 + * + * @param filePath 文件路径 + * @return 私钥 + */ + public String getPrivateKeyFromFile(String filePath) { + String strPrivateKey = ""; + try { + BufferedReader privateKey = new BufferedReader(new FileReader(filePath)); + String line = ""; + while ((line = privateKey.readLine()) != null) { + strPrivateKey += line; + } + privateKey.close(); + strPrivateKey = strPrivateKey.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", ""); + } catch (Exception e) { + e.printStackTrace(); + } + return strPrivateKey; + } + + /** + * 从.pem文件中取得公钥 + * + * @param filePath 文件路径 + * @return 公钥 + */ + public String getPublicKeyFromFile(String filePath) { + String strPublicKey = ""; + try { + BufferedReader publicKey = new BufferedReader(new FileReader(filePath)); + String line = ""; + while ((line = publicKey.readLine()) != null) { + strPublicKey += line; + } + publicKey.close(); + strPublicKey = strPublicKey.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", ""); + } catch (Exception e) { + e.printStackTrace(); + } + return strPublicKey; + } + + /** + * 从字符串中加载公钥 + * + * @param publicKeyStr 公钥数据字符串 + * @throws Exception 加载公钥时产生的异常 + */ + public void loadPublicKey(String publicKeyStr) throws Exception { + try { + byte[] buffer = Base64Utils.decode(publicKeyStr); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); + this.publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("公钥非法"); + } catch (NullPointerException e) { + throw new Exception("公钥数据为空"); + } + } + + /** + * 加载私钥 + * + * @param privateKeyStr 私钥文件名 + * @return 是否成功 + * @throws Exception + */ + public void loadPrivateKey(String privateKeyStr) throws Exception { + try { + byte[] buffer = Base64Utils.decode(privateKeyStr); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + this.privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("私钥非法"); + } catch (NullPointerException e) { + throw new Exception("私钥数据为空"); + } + } + + /** + * 加密过程 + * + * @param publicKey 公钥 + * @param plainTextData 明文数据 + * @return + * @throws Exception 加密过程中的异常信息 + */ + public byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) + throws Exception { + if (publicKey == null) { + throw new Exception("加密公钥为空, 请设置"); + } + Cipher cipher = null; + try { + cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] output = cipher.doFinal(plainTextData); + return output; + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此加密算法"); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + return null; + } catch (InvalidKeyException e) { + throw new Exception("加密公钥非法,请检查"); + } catch (IllegalBlockSizeException e) { + throw new Exception("明文长度非法"); + } catch (BadPaddingException e) { + throw new Exception("明文数据已损坏"); + } + } + + /** + * 解密过程 + * + * @param privateKey 私钥 + * @param cipherData 密文数据 + * @return 明文 + * @throws Exception 解密过程中的异常信息 + */ + public byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) + throws Exception { + if (privateKey == null) { + throw new Exception("解密私钥为空, 请设置"); + } + Cipher cipher = null; + try { + cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] output = cipher.doFinal(cipherData); + return output; + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此解密算法"); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + return null; + } catch (InvalidKeyException e) { + throw new Exception("解密私钥非法,请检查"); + } catch (IllegalBlockSizeException e) { + throw new Exception("密文长度非法"); + } catch (BadPaddingException e) { + throw new Exception("密文数据已损坏"); + } + } + + /** + * 字节数据转十六进制字符串 + * + * @param data 输入数据 + * @return 十六进制内容 + */ + public static String byteArrayToString(byte[] data) { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < data.length; i++) { + // 取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移 + stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]); + // 取出字节的低四位 作为索引得到相应的十六进制标识符 + stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]); + if (i < data.length - 1) { + stringBuilder.append(' '); + } + } + return stringBuilder.toString(); + } + + /** + * btye转换hex函数 + * + * @param byteArray + * @return + */ + public static String byteToHex(byte[] byteArray) { + StringBuffer strBuff = new StringBuffer(); + for (int i = 0; i < byteArray.length; i++) { + if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) { + strBuff.append("0").append( + Integer.toHexString(0xFF & byteArray[i])); + } else { + strBuff.append(Integer.toHexString(0xFF & byteArray[i])); + } + } + return strBuff.toString(); + } + + /** + * 以字节为单位读取文件,常用于读二进制文件,如图片、声音、影像等文件。 + */ + public static byte[] readFileByBytes(String fileName) { + File file = new File(fileName); + InputStream in = null; + byte[] txt = new byte[(int) file.length()]; + try { + // 一次读一个字节 + in = new FileInputStream(file); + int tempbyte; + int i = 0; + while ((tempbyte = in.read()) != -1) { + txt[i] = (byte) tempbyte; + i++; + } + in.close(); + return txt; + } catch (IOException e) { + e.printStackTrace(); + return txt; + } + } + + +// /** +// * Main 测试方法 +// * @param args +// */ +// public static void main(String[] args) throws FileNotFoundException, CertificateException { +// RsaEncrypt rsaEncrypt = new RsaEncrypt(); +// try { +// String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhdBzSJUS439zw+evI1Bg/dZsXqKuqeNNuT+QJXnnanfRmZ15EMZwUp2SiFAspPTqxuqVoHOEYWmcuVvmLPV0bWwZlvDTrhEVlGWAQM/tE4/z3V2+P2xw/F9YLUyjLr/0tcFAsiyEPvNPHM56arF3InWMQuJqetZabhcUoyO+XsEZx9L9UgRp+snlYbO5Ewq1/pJjxoPk6W7Sw3rW2dIiCNbBmcqS/hXvrhQGk3JtOyTVy159Q0yQTlD5vaC77SLmofXwHn8fk7ugAQAD4hYg/EjO4g2cBi8dGE1D9Ysxoj8aFilAyGWZe06YUUV7d8vh8pEwIprfHZVr8bN8+PB5wIDAQAB"; +// rsaEncrypt.loadPublicKey(publicKey); +// System.out.println("加载公钥成功"); +// } catch (Exception e) { +// System.err.println(e.getMessage()); +// System.err.println("加载公钥失败"); +// } +// +// // 加载私钥 +// try { +// String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDCF0HNIlRLjf3P\n" + +// "D568jUGD91mxeoq6p4025P5Aleedqd9GZnXkQxnBSnZKIUCyk9OrG6pWgc4RhaZy\n" + +// "5W+Ys9XRtbBmW8NOuERWUZYBAz+0Tj/PdXb4/bHD8X1gtTKMuv/S1wUCyLIQ+808\n" + +// "cznpqsXcidYxC4mp61lpuFxSjI75ewRnH0v1SBGn6yeVhs7kTCrX+kmPGg+TpbtL\n" + +// "DetbZ0iII1sGZypL+Fe+uFAaTcm07JNXLXn1DTJBOUPm9oLvtIuah9fAefx+Tu6A\n" + +// "BAAPiFiD8SM7iDZwGLx0YTUP1izGiPxoWKUDIZZl7TphRRXt3y+HykTAimt8dlWv\n" + +// "xs3z48HnAgMBAAECggEAMnoB7uuCqLOg0nzpd3f1FfD6LqID+k8g9qnPRdS2i9Zl\n" + +// "7tUuy9J3nEFVSiojvU2cut9TCXRwtzXe9/qbb0c10HhEDBrJTGu3ugs1y8Wgh6/i\n" + +// "AH44FsFDvgXL6Z62KstnRNoOv46aYLhbw+DjqtVy58whHFg30S0X79q2h/FuLYtQ\n" + +// "gju0I5yDMBVmbTmJKvhKtcATBUKnXxsAIMnc/BWXHNV9dg/Yfx1JDOqZqxFUsYmn\n" + +// "Mz5CatfGqnyc7AH1S0SFp4hIYsA1iPuEYDtFM8AAyP0PB4Z+buaG12xMF4S35OqD\n" + +// "P0uhYpsh8/vQ28xqn0hAGsA1lAb792nZ5Qf/0WQGgQKBgQDuTNJdyIQN9Oa/DrQw\n" + +// "DEPpyiurGBqY6ZvJHq1CpDy4AbMkIEYlVBnFPHCjyiWyW61MT80qMXyCQn3DLs5M\n" + +// "sbB3rNZYItD9Tqmd5vkxD3B4zRneH1FQUuWR64lqM0fjFiy7+gfpn3tC5169ynn+\n" + +// "u1oArwEnA577c93GHmr/P+I5FwKBgQDQgdCW8wTrLYLWrG7pg/VshWJX+J/8vGM8\n" + +// "b2WPF52hIX7HhIouQjiwhAcMam0vwg0ZiPJH0Rw67PWrPxz1GOtfBwSXD74j/0QG\n" + +// "do/NwN8HxcXoGRFCb44KbDKj7r9xIwQ7eir93S7wb6FWzfzM4hcgQy6lZQO1wA1l\n" + +// "d/n8GkOfsQKBgQDSZJUfJBgjUmQfr1W7TjjvwwWHC8L/2tEmYqVKP0M0mpuVe8Ey\n" + +// "1+vgzhoNIj7iGDsLMzHK3OI3B4mNuFeAMynAn6KnMiYeX0M2HGVKwXzlyv/ZswDR\n" + +// "0D7L9I7gauI/Rqa8WGaAB8SGL0zgeFH5a2RtUhXMYlANZiEZ+GUzcy3ChQKBgQCR\n" + +// "tDV0Vox4X4xPwZ2b9we49xi7CtWaAneCUwHwVXtkjwiOGhOJseL1jr1yMqjsN7l5\n" + +// "v5Y3E2ybzNz79xWgPPK1rCzL90dS1l3N+moRSRdR0iWzRFkoSNZEeEmG/x6uy954\n" + +// "Ra3nSvozMH7ZLwbakZ/BftEI6ScHihaScp2xSxU4cQKBgQDANT+6d3E6aGT2oAdT\n" + +// "xAdimcLEWZOVW9gkbX/UASEpTjEMDdr+MoPuKSPYxuMcWf/HksJF8rAVYJjklcIZ\n" + +// "HI1TF0Mm7RVrM+Kw3VzHR21jf7RNboBAAH+YeUH9yZg936iqO4Gi5wegUtttXgrx\n" + +// "xG5rwi8iUXbbiC/xLVm0ldHW5Q=="; +// rsaEncrypt.loadPrivateKey(privateKey); +// System.out.println("加载私钥成功"); +// } catch (Exception e) { +// System.err.println(e.getMessage()); +// System.err.println("加载私钥失败"); +// } +// //测试字符串 +// String encryptStr = "12321dsfasf1321312fsfdsafsdafasfsadf"; +// try { +// System.out.println(new Date()); +// System.out.println(new Date()); +// // 加密 +// byte[] cipher = rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(), +// encryptStr.getBytes()); +// // 解密 +// byte[] plainText = rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(), +// cipher); +// System.out.println(new Date()); +// System.out.println(new String(plainText)); +// byte[] content = readFileByBytes("/Users/a1234/Documents/ca/demo.zip"); +// // 签名验证 +// byte[] signbyte = rsaEncrypt.rsaSign(content, rsaEncrypt.getPrivateKey()); +// System.out.println("签名-----:" + byteToHex(signbyte)); +// ByteUtil.saveFile(signbyte,"/Users/a1234/Documents/ca/","demo.zip.sign"); +// Boolean isok = rsaEncrypt.verify(content, signbyte, rsaEncrypt.getPublicKey()); +// System.out.println("验证:" + isok); +// +// // 读取验证文件 +// byte[] read = readFileByBytes("/Users/a1234/Documents/ca/demo.zip.sign"); +// System.out.println("读取签名文件:" + byteToHex(signbyte)); +// Boolean isfok = rsaEncrypt.verify(content, read, rsaEncrypt.getPublicKey()); +// System.out.println("文件验证2:" + isfok); +// +// } catch (Exception e) { +// System.err.println(e.getMessage()); +// } +// } + +} \ No newline at end of file diff --git a/sf-common/src/main/java/com/sf/common/utils/sign/Sha265Utils.java b/sf-common/src/main/java/com/sf/common/utils/sign/Sha265Utils.java new file mode 100644 index 0000000..e0514ed --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/sign/Sha265Utils.java @@ -0,0 +1,26 @@ +package com.sf.common.utils.sign; + +import java.security.MessageDigest; + +/** + * Sha256加密方法 + * + * @author ztzh + */ +public class Sha265Utils +{ + private static final String SHA_256_ALGORITHM = "SHA-256"; + public static String encrypt(byte[] data) throws Exception { + //获取SHA-256算法实例 + MessageDigest messageDigest = MessageDigest.getInstance(SHA_256_ALGORITHM); + //计算散列值 + byte[] digest = messageDigest.digest(data); + StringBuilder stringBuilder = new StringBuilder(); + //将byte数组转换为15进制字符串 + for (byte b : digest) { + stringBuilder.append(Integer.toHexString((b & 0xFF) | 0x100).toUpperCase(), 1, 3); + } + return stringBuilder.toString(); + } + +} diff --git a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java index 379ec92..49cd5f5 100644 --- a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java +++ b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java @@ -124,7 +124,7 @@ public class SysOssController extends BaseController * * @param file 文件 */ - //@PreAuthorize("@ss.hasPermi('system:oss:upload')") + @PreAuthorize("@ss.hasPermi('system:oss:upload')") @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R upload(@RequestPart("file") MultipartFile file) { @@ -140,6 +140,21 @@ public class SysOssController extends BaseController return R.ok(oss); } + + @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) + @PostMapping(value = "/zip/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R zipUpload(@RequestPart("file") MultipartFile file) throws Exception { + if (ObjectUtil.isNull(file)) { + throw new ServiceException("上传文件不能为空"); + } + long size = file.getSize(); + if (size > defaultMaxSize) { + throw new FileSizeLimitExceededException(defaultMaxSize / 1024 / 1024); + } + SysOss oss = sysOssService.uploadSignZip(file); + return R.ok(oss); + } + /** * 下载OSS对象 * diff --git a/sf-file/src/main/java/com/sf/file/service/ISysOssService.java b/sf-file/src/main/java/com/sf/file/service/ISysOssService.java index 34083d1..6dfcd03 100644 --- a/sf-file/src/main/java/com/sf/file/service/ISysOssService.java +++ b/sf-file/src/main/java/com/sf/file/service/ISysOssService.java @@ -1,6 +1,8 @@ package com.sf.file.service; import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.List; import javax.servlet.http.HttpServletResponse; @@ -69,4 +71,6 @@ public interface ISysOssService { SysOss upload(File file); void download(String ossId, HttpServletResponse response) throws Exception; + + SysOss uploadSignZip(MultipartFile file) throws Exception; } diff --git a/sf-file/src/main/java/com/sf/file/service/impl/SysOssServiceImpl.java b/sf-file/src/main/java/com/sf/file/service/impl/SysOssServiceImpl.java index 10a9e4f..a937a9b 100644 --- a/sf-file/src/main/java/com/sf/file/service/impl/SysOssServiceImpl.java +++ b/sf-file/src/main/java/com/sf/file/service/impl/SysOssServiceImpl.java @@ -1,13 +1,12 @@ package com.sf.file.service.impl; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.InputStream; +import java.io.*; import java.util.List; import java.util.UUID; import javax.servlet.http.HttpServletResponse; +import com.sf.common.utils.openssl.RsaSignUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -162,6 +161,10 @@ public class SysOssServiceImpl implements ISysOssService { throw new ServiceException(e.getMessage()); } } + @Override + public SysOss uploadSignZip(MultipartFile file) throws Exception { + return upload(RsaSignUtil.signZip(file)); + } public UploadResult uploadSuffix(File file, String suffix) { return upload(file, getPath(null, suffix)); From d74454131e02ec5297c17affeebb2c692fc7a8d7 Mon Sep 17 00:00:00 2001 From: pengren Date: Thu, 18 Apr 2024 18:04:22 +0800 Subject: [PATCH 16/16] =?UTF-8?q?=E7=BC=96=E5=8F=B7=EF=BC=9AZSSAC-163=20?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=9A=E7=A6=BB=E7=BA=BF=E5=8C=85=E7=AD=BE?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/sf/file/controller/SysOssController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java index 49cd5f5..5d332a4 100644 --- a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java +++ b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java @@ -152,6 +152,7 @@ public class SysOssController extends BaseController throw new FileSizeLimitExceededException(defaultMaxSize / 1024 / 1024); } SysOss oss = sysOssService.uploadSignZip(file); + oss.setSize(file.getSize()); return R.ok(oss); }