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 @@ - - - + + +