diff --git a/sf-admin/pom.xml b/sf-admin/pom.xml index 3603c72..49f0926 100644 --- a/sf-admin/pom.xml +++ b/sf-admin/pom.xml @@ -66,6 +66,13 @@ sf-oauth + + com.smarterFramework + sf-order + + + + 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 new file mode 100644 index 0000000..8454cf7 --- /dev/null +++ b/sf-admin/src/main/java/com/sf/web/controller/system/UserMemberController.java @@ -0,0 +1,113 @@ +package com.sf.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.system.domain.UserMember; +import com.sf.system.service.IUserMemberService; +import com.sf.common.utils.poi.ExcelUtil; +import com.sf.common.core.page.TableDataInfo; + +/** + * 会员Controller + * + * @author ztzh + * @date 2024-04-16 + */ +@RestController +@RequestMapping("/system/member") +public class UserMemberController extends BaseController +{ + @Autowired + private IUserMemberService userMemberService; + + /** + * 查询会员列表 + */ + @PreAuthorize("@ss.hasPermi('system:member:list')") + @GetMapping("/list") + public TableDataInfo list(UserMember userMember) + { + startPage(); + List list = userMemberService.selectUserMemberList(userMember); + return getDataTable(list); + } + + /** + * 导出会员列表 + */ + @PreAuthorize("@ss.hasPermi('system:member:export')") + @Log(title = "会员", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, UserMember userMember) + { + List list = userMemberService.selectUserMemberList(userMember); + ExcelUtil util = new ExcelUtil(UserMember.class); + util.exportExcel(response, list, "会员数据"); + } + + /** + * 获取会员详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:member:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(userMemberService.selectUserMemberById(id)); + } + + /** + * 获取会员详细信息,通过用户id + */ + @GetMapping(value = "/byUser") + public AjaxResult getInfoByUser(Long userId) + { + return success(userMemberService.selectUserMemberByUserId(userId)); + } + + /** + * 新增会员 + */ + @PreAuthorize("@ss.hasPermi('system:member:add')") + @Log(title = "会员", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody UserMember userMember) + { + return toAjax(userMemberService.insertUserMember(userMember)); + } + + /** + * 修改会员 + */ + @PreAuthorize("@ss.hasPermi('system:member:edit')") + @Log(title = "会员", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody UserMember userMember) + { + return toAjax(userMemberService.updateUserMember(userMember)); + } + + /** + * 删除会员 + */ + @PreAuthorize("@ss.hasPermi('system:member:remove')") + @Log(title = "会员", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(userMemberService.deleteUserMemberByIds(ids)); + } +} diff --git a/sf-framework/pom.xml b/sf-framework/pom.xml index df42e74..480011e 100644 --- a/sf-framework/pom.xml +++ b/sf-framework/pom.xml @@ -72,6 +72,18 @@ com.smarterFramework sf-common + + + io.springfox + springfox-boot-starter + + + + + io.swagger + swagger-models + 1.6.2 + \ No newline at end of file diff --git a/sf-order/pom.xml b/sf-order/pom.xml new file mode 100644 index 0000000..d95e016 --- /dev/null +++ b/sf-order/pom.xml @@ -0,0 +1,33 @@ + + + + smarterFramework + com.smarterFramework + 1.0.0 + + 4.0.0 + + sf-order + + + 订单模块 + + + + + + + com.smarterFramework + sf-common + + + com.smarterFramework + sf-framework + + + + + + \ No newline at end of file diff --git a/sf-admin/src/main/java/com/sf/order/controller/OrderInfoController.java b/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java similarity index 91% rename from sf-admin/src/main/java/com/sf/order/controller/OrderInfoController.java rename to sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java index d887548..21f3db2 100644 --- a/sf-admin/src/main/java/com/sf/order/controller/OrderInfoController.java +++ b/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java @@ -13,7 +13,6 @@ import com.sf.order.domain.dto.OrderCreateDto; 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; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -39,10 +38,7 @@ public class OrderInfoController extends BaseController { @GetMapping("/list") public TableDataInfo list(OrderListReqVo vo) { startPage(); - SysUser user = SecurityUtils.getLoginUser().getUser(); - vo.setUserId(user.getUserId()); List list = orderInfoService.queryList(vo); - logger.info("prderList"+list); return getDataTable(list); } @@ -72,7 +68,7 @@ public class OrderInfoController extends BaseController { @Log(title = "创建订单基础信息", businessType = BusinessType.INSERT) @PostMapping(value = "/createOrder") public AjaxResult createOrder(@RequestBody OrderCreateDto orderCreateDto) { - return toAjax(orderInfoService.createOrder(orderCreateDto)); + return AjaxResult.success(orderInfoService.createOrder(orderCreateDto)); } /** @@ -87,7 +83,6 @@ public class OrderInfoController extends BaseController { /** * 支付订单 */ - @Operation(summary = "支付订单") @PostMapping(value = "/pay/{orderId}") private String orderPay(@PathVariable(value = "orderId") Long orderId) { orderInfoService.orderPay(orderId); diff --git a/sf-admin/src/main/java/com/sf/order/domain/OrderInfo.java b/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java similarity index 92% rename from sf-admin/src/main/java/com/sf/order/domain/OrderInfo.java rename to sf-order/src/main/java/com/sf/order/domain/OrderInfo.java index 6e165a8..6785f83 100644 --- a/sf-admin/src/main/java/com/sf/order/domain/OrderInfo.java +++ b/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java @@ -26,16 +26,12 @@ public class OrderInfo extends BaseEntity { * 订单编号 */ @Excel(name = "订单编号") - private Long orderNo; + private String orderNo; /** - * 订单状态: - * 0:待支付 - * 1:已付款 - * 2:支付超时系统结束 - * 3:已完成 + * 订单状态: 0:待支付 1:已付款待发货 2:配送中 3:待取货 4:支付超时系统结束 5:客户自主取消 6:已完成 */ - @Excel(name = "订单状态: 0:待支付 1:已付款 2:支付超时系统结束 3:已完成 ") + @Excel(name = "订单状态: 0:待支付 1:已付款待发货 2:配送中 3:待取货 4:支付超时系统结束 5:客户自主取消 6:已完成 ") private Long orderStatus; /** @@ -105,8 +101,8 @@ public class OrderInfo extends BaseEntity { /** * 支付时间 */ - @JsonFormat(pattern = "yyyy-MM-dd") - @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date payTime; /** @@ -145,7 +141,7 @@ public class OrderInfo extends BaseEntity { * 平台外部订单号 */ @Excel(name = "平台外部订单号") - private Long outOrderNo; + private String outOrderNo; /** * 平台支付返回值 @@ -167,11 +163,11 @@ public class OrderInfo extends BaseEntity { return id; } - public void setOrderNo(Long orderNo) { + public void setOrderNo(String orderNo) { this.orderNo = orderNo; } - public Long getOrderNo() { + public String getOrderNo() { return orderNo; } @@ -311,11 +307,11 @@ public class OrderInfo extends BaseEntity { return orderType; } - public void setOutOrderNo(Long outOrderNo) { + public void setOutOrderNo(String outOrderNo) { this.outOrderNo = outOrderNo; } - public Long getOutOrderNo() { + public String getOutOrderNo() { return outOrderNo; } diff --git a/sf-admin/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java b/sf-order/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java similarity index 79% rename from sf-admin/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java rename to sf-order/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java index 0359751..4ec904a 100644 --- a/sf-admin/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java +++ b/sf-order/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java @@ -1,5 +1,6 @@ package com.sf.order.domain.dto; +import com.sf.common.annotation.Excel; import io.swagger.v3.oas.annotations.media.Schema; import javax.validation.constraints.NotNull; @@ -15,12 +16,13 @@ public class OrderCreateDto { @NotNull(message = "商品id不能为空") private Long goodsId; + @Schema(description = "平台外部订单号") + private String outOrderNo; + @Schema(description = "数量") - @NotNull(message = "数量不能唯恐") private Long count; @Schema(description = "金额") - @NotNull(message = "金额不能为空") private Long amount; @Schema(description = "用户id") @@ -56,4 +58,12 @@ public class OrderCreateDto { public void setUserId(Long userId) { this.userId = userId; } + + public String getOutOrderNo() { + return outOrderNo; + } + + public void setOutOrderNo(String outOrderNo) { + this.outOrderNo = outOrderNo; + } } diff --git a/sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java b/sf-order/src/main/java/com/sf/order/domain/req/OrderListReqVo.java similarity index 94% rename from sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java rename to sf-order/src/main/java/com/sf/order/domain/req/OrderListReqVo.java index c0e0d17..11d3832 100644 --- a/sf-admin/src/main/java/com/sf/order/domain/req/OrderListReqVo.java +++ b/sf-order/src/main/java/com/sf/order/domain/req/OrderListReqVo.java @@ -14,7 +14,7 @@ public class OrderListReqVo extends BaseEntity { private Long userId; /** - * 订单类型 + * 订单状态 */ private Long orderStatus; diff --git a/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java b/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java similarity index 53% rename from sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java rename to sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java index 1c7a63d..6bea013 100644 --- a/sf-admin/src/main/java/com/sf/order/domain/res/OrderListResVo.java +++ b/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java @@ -1,8 +1,12 @@ package com.sf.order.domain.res; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.sf.common.annotation.Excel; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.util.Date; + /** * 活动信息详情 * @@ -21,11 +25,7 @@ public class OrderListResVo { private String orderNo; /** - * 订单状态: - * 0:待支付 - * 1:已付款 - * 2:支付超时系统结束 - * 3:已完成 + * 订单状态: 0:待支付 1:已付款待发货 2:配送中 3:待取货 4:支付超时系统结束 5:客户自主取消 6:已完成 */ private Long orderStatus; @@ -34,6 +34,18 @@ public class OrderListResVo { */ private Long orderAmt; + /** + * 支付时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date payTime; + + /** + * 订阅订单取消时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date subscriptionCancellationTime; + /** * 商品标题 */ @@ -43,13 +55,21 @@ public class OrderListResVo { */ private String productPicture; + /** + * 商品描述 + */ + private String productDesc; + + /** * 商品规格 */ private String goodsSpec; + /** * 商品数量 */ - private Integer goodsCount = 1; + private Integer goodsCount; + } diff --git a/sf-admin/src/main/java/com/sf/order/mapper/OrderInfoMapper.java b/sf-order/src/main/java/com/sf/order/mapper/OrderInfoMapper.java similarity index 96% rename from sf-admin/src/main/java/com/sf/order/mapper/OrderInfoMapper.java rename to sf-order/src/main/java/com/sf/order/mapper/OrderInfoMapper.java index dfb2950..342981a 100644 --- a/sf-admin/src/main/java/com/sf/order/mapper/OrderInfoMapper.java +++ b/sf-order/src/main/java/com/sf/order/mapper/OrderInfoMapper.java @@ -64,4 +64,6 @@ public interface OrderInfoMapper { * @return 结果 */ public int deleteOrderInfoByIds(Long[] ids); + + OrderInfo selectOrderInfoByOrderNo(String orderNo); } diff --git a/sf-admin/src/main/java/com/sf/order/service/IOrderInfoService.java b/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java similarity index 93% rename from sf-admin/src/main/java/com/sf/order/service/IOrderInfoService.java rename to sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java index 26210aa..16d34ef 100644 --- a/sf-admin/src/main/java/com/sf/order/service/IOrderInfoService.java +++ b/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java @@ -43,7 +43,7 @@ public interface IOrderInfoService * @param orderInfo 订单基础信息 * @return 结果 */ - public int createOrder(OrderCreateDto orderInfo); + public Long createOrder(OrderCreateDto orderInfo); /** * 修改订单基础信息 @@ -70,4 +70,6 @@ public interface IOrderInfoService public int deleteOrderInfoById(Long id); void orderPay(Long orderId); + + OrderInfo selectOrderInfoByOrderNo(String orderNo); } diff --git a/sf-admin/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java b/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java similarity index 79% rename from sf-admin/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java rename to sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java index 5e3edda..24a723a 100644 --- a/sf-admin/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java +++ b/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java @@ -21,8 +21,7 @@ import java.util.List; * @date 2024-04-09 */ @Service -public class OrderInfoServiceImpl implements IOrderInfoService -{ +public class OrderInfoServiceImpl implements IOrderInfoService { @Resource private OrderInfoMapper orderInfoMapper; @Resource @@ -35,8 +34,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService * @return 订单基础信息 */ @Override - public OrderInfo selectOrderInfoById(Long id) - { + public OrderInfo selectOrderInfoById(Long id) { return orderInfoMapper.selectOrderInfoById(id); } @@ -47,12 +45,11 @@ public class OrderInfoServiceImpl implements IOrderInfoService * @return 订单基础信息 */ @Override - public List selectOrderInfoList(OrderInfo orderInfo) - { + public List selectOrderInfoList(OrderInfo orderInfo) { return orderInfoMapper.selectOrderInfoList(orderInfo); } - public List queryList(OrderListReqVo vo) - { + + public List queryList(OrderListReqVo vo) { return orderInfoMapper.queryList(vo); } @@ -63,11 +60,10 @@ public class OrderInfoServiceImpl implements IOrderInfoService * @return 结果 */ @Override - public int createOrder(OrderCreateDto orderCreateDto) - { + public Long createOrder(OrderCreateDto orderCreateDto) { OrderInfo orderInfo = new OrderInfo(); - orderInfo.setOrderNo(snowflakeIdWorker.nextId()); - orderInfo.setPayType(0L); + orderInfo.setOrderNo(snowflakeIdWorker.nextId() + ""); + orderInfo.setPayType(1L); orderInfo.setReceiveType(0L); orderInfo.setOrderStatus(0L); orderInfo.setCreateUserId(orderCreateDto.getUserId()); @@ -77,7 +73,9 @@ public class OrderInfoServiceImpl implements IOrderInfoService orderInfo.setPayAmt(orderCreateDto.getAmount()); orderInfo.setCreateTime(DateUtils.getNowDate()); orderInfo.setUpdateTime(DateUtils.getNowDate()); - return orderInfoMapper.insertOrderInfo(orderInfo); + orderInfo.setOutOrderNo(orderCreateDto.getOutOrderNo()); + orderInfoMapper.insertOrderInfo(orderInfo); + return snowflakeIdWorker.nextId(); } /** @@ -87,11 +85,11 @@ public class OrderInfoServiceImpl implements IOrderInfoService * @return 结果 */ @Override - public int updateOrderInfo(OrderInfo orderInfo) - { + public int updateOrderInfo(OrderInfo orderInfo) { orderInfo.setUpdateTime(DateUtils.getNowDate()); return orderInfoMapper.updateOrderInfo(orderInfo); } + @Override public void orderPay(Long orderId) { OrderInfo updateOrder = this.selectOrderInfoById(orderId); @@ -104,11 +102,17 @@ public class OrderInfoServiceImpl implements IOrderInfoService updateOrder.setOrderStatus(1L); updateOrder.setPayTime(DateUtils.getNowDate()); // 修改订单状态 - if (1>this.updateOrderInfo(updateOrder)) { + if (1 > this.updateOrderInfo(updateOrder)) { throw new ServiceException("支付异常,请联系管理员!"); } } + @Override + public OrderInfo selectOrderInfoByOrderNo(String orderNo) { + + return orderInfoMapper.selectOrderInfoByOrderNo(orderNo); + } + /** * 批量删除订单基础信息 * @@ -116,8 +120,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService * @return 结果 */ @Override - public int deleteOrderInfoByIds(Long[] ids) - { + public int deleteOrderInfoByIds(Long[] ids) { return orderInfoMapper.deleteOrderInfoByIds(ids); } @@ -128,8 +131,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService * @return 结果 */ @Override - public int deleteOrderInfoById(Long id) - { + public int deleteOrderInfoById(Long id) { return orderInfoMapper.deleteOrderInfoById(id); } diff --git a/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml b/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml similarity index 92% rename from sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml rename to sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml index 207b139..51117d1 100644 --- a/sf-admin/src/main/resources/mapper/order/OrderInfoMapper.xml +++ b/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml @@ -35,18 +35,21 @@ + + + - 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, 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 + 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 + FROM Order_info a LEFT JOIN GOODS_MESSAGES b ON a.goods_id = b.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