php会员系统源码_b2b2c商城系统会员预存款架构及源码分享

业务需求

  • 可以为预存款充值,在支付订单时使用预存款支付功能

  • 当预存款余额>商品订单总金额时,完全抵扣商品订单金额;

  • 当预存款余额

架构

一、 充值

13da18f285487b8c5639eafff4d4aab9.png

二、 数据结构

1、会员钱包表(es_member_wallet)

2、 后期可能会将会员积分等关于消费抵扣相关信息放入此表中

a158f65eb3df7aeaddf4927bc9f440b0.png

3、充值记录表(es_deposite_recharge)

f1dd7257e8d6b7c91fd3cc2c683bd843.png

4.预存款日志表(es_deposite_log)

3e62242c713b1918e6a8182c0611c9cc.png

三、充值模型图

67c4df6d5f78716d0f0c3bd20f8d32e2.png

四、源码

说明:此处仅展示预存款充值相关代码,其他关联业务不做具体展示

预存款充值相关API

package com.enation.app.javashop.buyer.api.payment;

import com.enation.app.javashop.core.payment.model.dto.PayParam;

import com.enation.app.javashop.core.payment.service.OrderPayManager;

import com.enation.app.javashop.core.trade.deposite.model.dos.RechargeDO;

import com.enation.app.javashop.core.trade.deposite.service.RechargeManager;

import com.enation.app.javashop.core.trade.order.model.enums.TradeTypeEnum;

import io.swagger.annotations.Api;

import io.swagger.annotations.ApiImplicitParam;

import io.swagger.annotations.ApiImplicitParams;

import io.swagger.annotations.ApiOperation;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.validation.annotation.Validated;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.Max;

import javax.validation.constraints.Min;

import java.util.Map;

/**

* @description: 预存款充值

* @author: liuyulei

* @create: 2019-12-30 19:53

* @version:1.0

* @since:7.1.4

**/

@Api(description = "预存款充值相关API")

@RestController

@RequestMapping("/recharge")

@Validated

public class RechargePayBuyerController {

@Autowired

private RechargeManager rechargeManager;

@Autowired

private OrderPayManager orderPayManager;

@PostMapping

@ApiOperation(value = "创建充值订单")

@ApiImplicitParams({

@ApiImplicitParam(name = "price", value = "充值金额", required = true, dataType = "double", paramType = "query")

})

public RechargeDO create(@Max(value = 10000,message = "充值金额输入有误,单次最多允许充值10000元") @Min(value = 1, message = "充值金额有误,单次最少充值金额为1元") Double price) {

return this.rechargeManager.recharge(price);

}

@PostMapping(value = "/{sn}")

@ApiOperation(value = "支付充值订单")

@ApiImplicitParams({

@ApiImplicitParam(name = "sn", value = "充值订单编号", required = true, dataType = "String", paramType = "path")

})

public Map pay(@PathVariable(name = "sn") String sn, @Validated PayParam payParam) {

payParam.setSn(sn);

payParam.setTradeType(TradeTypeEnum.RECHARGE.name());

return orderPayManager.pay(payParam);

}

}

充值相关业务

package com.enation.app.javashop.core.trade.deposite.service.impl;

import com.enation.app.javashop.core.client.member.DepositeClient;

import com.enation.app.javashop.core.member.model.dto.DepositeParamDTO;

import com.enation.app.javashop.core.payment.model.dos.PaymentBillDO;

import com.enation.app.javashop.core.payment.service.PaymentBillManager;

import com.enation.app.javashop.core.statistics.util.DateUtil;

import com.enation.app.javashop.core.trade.TradeErrorCode;

import com.enation.app.javashop.core.trade.deposite.model.dos.RechargeDO;

import com.enation.app.javashop.core.trade.deposite.service.RechargeManager;

import com.enation.app.javashop.core.trade.order.model.enums.PayStatusEnum;

import com.enation.app.javashop.core.trade.order.model.enums.TradeTypeEnum;

import com.enation.app.javashop.framework.context.UserContext;

import com.enation.app.javashop.framework.exception.ServiceException;

import com.enation.app.javashop.framework.security.model.Buyer;

import com.enation.app.javashop.framework.util.CurrencyUtil;

import com.enation.app.javashop.framework.util.SqlSplicingUtil;

import com.enation.app.javashop.framework.util.StringUtil;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Propagation;

import org.springframework.transaction.annotation.Transactional;

import com.enation.app.javashop.framework.database.DaoSupport;

import com.enation.app.javashop.framework.database.Page;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

/**

* 充值记录业务类

* @author liuyulei

* @version v1.0

* @since v7.1.5

* 2019-12-30 16:38:45

*/

@Service

public class RechargeManagerImpl implements RechargeManager {

@Autowired

@Qualifier("tradeDaoSupport")

private DaoSupport daoSupport;

@Autowired

private PaymentBillManager paymentBillManager;

@Autowired

private DepositeClient depositeClient;

@Override

@Transactional(value = "tradeTransactionManager",propagation = Propagation.REQUIRED,rollbackFor=Exception.class)

public RechargeDO recharge(Double money) {

Buyer buyer = UserContext.getBuyer();

//本金额支付次数

int times = 0;

//账单编号格式:年月日+ memberID掩码 + 价格掩码 +支付次数

//掩码均为5位数,不足前面补零

String snPrefix = createSn(buyer.getUid(), "D", money);

String sn = snPrefix + times;

RechargeDO rechargeDO = this.getModel(sn);

if(rechargeDO == null ){

//整合充值订单数据

rechargeDO = new RechargeDO(sn,buyer.getUid(),buyer.getUsername(),money);

//添加充值订单

createBill(rechargeDO);

}else{

//如果是已付款

if(!PayStatusEnum.PAY_NO.name().equals(rechargeDO.getPayStatus())){

// 获取到已支付次数

times = Integer.parseInt(sn.substring(19, sn.length()));

//循环生成sn

while (true) {

times++;

sn = snPrefix + times;

RechargeDO rechargeTemp = this.getModel(sn);

// 找到一个没有使用过的 就可以break了

if (rechargeTemp == null) {

break;

}

}

//充值订单已经被支付,则表示当天再次支付

rechargeDO.setRechargeSn(sn);

rechargeDO.setPayStatus(PayStatusEnum.PAY_NO.name());

createBill(rechargeDO);

}

//如果没有被支付则不用创建充值订单,再次为此订单支付即可

}

return rechargeDO;

}

@Override

@Transactional(value = "tradeTransactionManager",propagation = Propagation.REQUIRED,rollbackFor=Exception.class)

public void paySuccess(String sn, Double price) {

RechargeDO rechargeDO = this.getModel(sn);

if(!rechargeDO.getRechargeMoney().equals(price)){

throw new ServiceException(TradeErrorCode.E454.code(), "付款金额和应付金额不一致");

}

this.daoSupport.execute("update es_deposite_recharge set pay_status = ? where recharge_sn = ? ",PayStatusEnum.PAY_YES.name(),sn);

//增加会员预存款 余额

depositeClient.increase(price,rechargeDO.getMemberId(),"会员充值,充值单号:" + rechargeDO.getRechargeSn());

}

@Override

@Transactional(value = "tradeTransactionManager",propagation = Propagation.REQUIRED,rollbackFor=Exception.class)

public void updatePaymentMethod(String subSn, String pluginId, String methodName) {

String sql = "update es_deposite_recharge set payment_plugin_id = ?,recharge_way = ?, pay_time = ? where recharge_sn = ? ";

this.daoSupport.execute(sql,pluginId,methodName, DateUtil.getDateline(),subSn);

}

@Override

public Page list(DepositeParamDTO paramDTO){

StringBuffer sql = new StringBuffer("select * from es_deposite_recharge ");

List whereSql = new ArrayList<>();
List term = new ArrayList<>();//根据会员名称查询if(!StringUtil.isEmpty(paramDTO.getMemberName())){
whereSql.add(" member_name = ? ");
term.add(paramDTO.getMemberName());
}
//根据会员id查询if(paramDTO.getMemberId() != null ){
whereSql.add(" member_id = ? ");
term.add(paramDTO.getMemberId());
}
//根据充值编号查询if(!StringUtil.isEmpty(paramDTO.getSn()) ){
whereSql.add(" recharge_sn = ? ");
term.add(paramDTO.getSn());
}
//根据充值编号查询if(!StringUtil.isEmpty(paramDTO.getSn()) ){
whereSql.add(" recharge_sn = ? ");
term.add(paramDTO.getSn());
}
//根据充值时间查询if(paramDTO.getStartTime() != null){
whereSql.add(" recharge_time >= ? ");
term.add(paramDTO.getStartTime());
}
//根据充值时间查询if(paramDTO.getStartTime() != null){
whereSql.add(" recharge_time <= ? ");
term.add(paramDTO.getEndTime());
}
whereSql.add(" pay_status = ? ");
term.add(PayStatusEnum.PAY_YES.name());//拼接sql
sql.append(SqlSplicingUtil.sqlSplicing(whereSql));
sql.append("order by recharge_time desc");
Page webPage = this.daoSupport.queryForPage(sql.toString(),paramDTO.getPageNo(), paramDTO.getPageSize() , RechargeDO.class,term.toArray() );return webPage;
}
@Override
public Double getPrice(String sn) {return this.daoSupport.queryForDouble("select recharge_money from es_deposite_recharge where recharge_sn = ? ",sn);
}
@Override
public RechargeDO getModel(String sn) {return this.daoSupport.queryForObject("select * from es_deposite_recharge where recharge_sn = ? ",RechargeDO.class,sn);
}
private String mask(String str) {
String mask = "000000";
mask = mask + str;
mask = mask.substring(mask.length() - 5);return mask;
}
private String createSn(Integer memberId,String prefix,Double price) {
String memberMask = mask("" + memberId);
String priceMask = mask("" + CurrencyUtil.mul(price,100).intValue());
String snPrefix = prefix + DateUtil.toString(new Date(), "yyyyMMdd") + memberMask + priceMask;return snPrefix;
}
private void createBill(RechargeDO rechargeDO) {
daoSupport.insert(rechargeDO);//创建充值 支付 账单数据
PaymentBillDO paymentBillDO = new PaymentBillDO();
paymentBillDO.setSubSn(rechargeDO.getRechargeSn());
paymentBillDO.setTradePrice(rechargeDO.getRechargeMoney());
paymentBillDO.setServiceType(TradeTypeEnum.RECHARGE.name());
paymentBillManager.add(paymentBillDO);
}
}

消费

一、消费时序图

9475a64804f52edeeed5eb911d49c72c.png

二、消费数据结构

1.交易表(es_trade)

新增预存款抵扣金额字段,记录订单使用的预存款金额

1bfdc445c80a13cac208d9de5593ce34.png

2.订单表(es_order)

新增预存款抵扣金额字段,记录订单使用的预存款金额

0b63c7a225c194f225f4173c0beecc25.png

3.源码

说明:此处仅展示预存款消费相关代码,其他关联业务不做具体展示

预存款支付相关API

@ApiOperation(value = "使用预存款支付")
@ApiImplicitParams({
@ApiImplicitParam(name = "sn", value = "要支付的交易sn", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "trade_type", value = "交易类型", required = true, dataType = "String", paramType = "query", allowableValues = "TRADE,ORDER"),
@ApiImplicitParam(name = "password", value = "支付密码", required = true, dataType = "String", paramType = "query")
})
@GetMapping(value = "/{trade_type}/{sn}")
public BalancePayVO payTrade(@PathVariable(name = "sn") String sn, @PathVariable(name = "trade_type") String tradeType,
@NotEmpty(message = "密码不能为空") String password) {
Buyer buyer = UserContext.getBuyer();
return balanceManager.balancePay(sn,buyer.getUid(),tradeType.toUpperCase(),password);
}

@GetMapping(value = "/cashier")
@ApiOperation(value = "获取预存款相关,收银台使用")
public MemberDepositeVO getDepositeVO() {
Buyer buyer = UserContext.getBuyer();
return depositeManager.getDepositeVO(buyer.getUid());
}
————————————————
版权声明:本文为CSDN博主「kingapex1」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kingapex1/article/details/105943918

预存款支付相关业务

package com.enation.app.javashop.core.trade.order.service.impl;

import com.enation.app.javashop.core.client.member.DepositeClient;

import com.enation.app.javashop.core.member.model.dos.MemberWalletDO;

import com.enation.app.javashop.core.payment.service.PaymentServicePlugin;

import com.enation.app.javashop.core.trade.order.model.vo.BalancePayVO;

import com.enation.app.javashop.core.trade.order.service.BalanceManager;

import com.enation.app.javashop.core.trade.order.service.TradeQueryManager;

import com.enation.app.javashop.framework.util.CurrencyUtil;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import java.util.List;

/**

* @description: 预存款抵扣业务类

* @author: liuyulei

* @create: 2020-01-01 11:55

* @version:1.0

* @since:7.1.4

**/

@Service

public class BalanceManagerImpl implements BalanceManager {

@Autowired

private TradeQueryManager tradeQueryManager;

@Autowired

private DepositeClient depositeClient;

@Autowired

private List paymentServicePlugin;@Overridepublic BalancePayVO balancePay(String sn,Integer memberId, String tradeType, String password) {//检测订单、交易是否属于当前登录会员 this.tradeQueryManager.checkIsOwner(sn,memberId); //检测支付密码是否正确 this.depositeClient.checkPwd(memberId,password); PaymentServicePlugin plugin = this.findServicePlugin(tradeType); //获取订单待支付金额 Double needPay = plugin.getPrice(sn); //获取会员预存款信息 MemberWalletDO walletDO = this.depositeClient.getModel(memberId); //判断预存款余额与订单待支付金额 Double diffPrice = CurrencyUtil.sub(needPay,walletDO.getPreDeposite()); Double balance = 0D; if(diffPrice >= 0 ){ //此时预存款不足,无法完全抵扣所有订单支付基恩 balance = walletDO.getPreDeposite(); needPay = diffPrice; }else{ //此时订单支付金额为0 balance = needPay; needPay = 0D; } BalancePayVO payVO = new BalancePayVO(); payVO.setSn(sn); payVO.setBalance(balance); payVO.setNeedPay(needPay); //预存款支付,修改订单待支付金额 plugin.balancePay(payVO,memberId); return payVO; } /** * 在支付子业务插件中 找到对应业务插件 * @param tradeType * @return */ private PaymentServicePlugin findServicePlugin(String tradeType) { for (PaymentServicePlugin plugin:paymentServicePlugin){ if (tradeType.equals(plugin.getServiceType())) { return plugin; } } return null; }}

更多源码分享,请关注“易族智汇”公众号查看更多文章!!

易族智汇(javashop)原创文章

e926c60c5eb973fcf82f3d9ce09fcbc1.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/571459.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML5 规范

在学习编程的时候&#xff0c;每次看到那些整齐规范的代码&#xff0c;心里顿时对这个程序员表示点点好感&#xff0c;有时&#xff0c;比如看到自己和朋友写的代码时&#xff0c;那阅读起来就是苦不堪言&#xff0c;所以&#xff0c;一些基本的开发规范是必须的&#xff0c;是…

JWT.NET的使用

JWT.NET的使用 原文:JWT.NET的使用JWT是什么 JWT全称是Json Web Token&#xff0c;是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准&#xff08; RFC 7519 &#xff09;&#xff0c;定义了一种简洁的&#xff0c;自包含的方法用于通信…

正在搜索需要的文件_【数澜发布-澜寻】智能内容搜索引擎,释放“机器阅读” 潜力...

4月23日&#xff0c;又是一年世界读书日。知识传播的重要性在这一天被无数次提起&#xff0c;记录、阅读成为我们 “理解”这个世界的加速通道&#xff0c;但在科技力量迅猛发展的当下&#xff0c;阅读这件事早已不再局限于人类。人工智能应用到社会中很重要的一步就是模仿人类…

学习笔记(十一)——数据库的索引碎片、计划缓存、统计信息

1.索引碎片 数据库存储本身是无序的&#xff0c;建立了聚集索引&#xff0c;会按照聚集索引物理顺序存入硬盘。既键值的逻辑顺序决定了表中相应行的物理顺序 而且在大多数的情况下&#xff0c;数据库写入频率远低于读取频率&#xff0c;索引的存在为了读取速度牺牲写入速度&…

网络软文发布软件_星浪中合网软文写出爆款汽车营销软文的特点是什么?

在移动互联网快速发展的时代&#xff0c;人们都是使用移动设备来获取外界信息。过去&#xff0c;许多广告汽车公司都专注于线下广告&#xff0c;但是现在他们已经将广告费用投入了移动互联网市场。移动互联网营销方法很多&#xff0c;例如&#xff0c;软文营销是一种非常强大的…

通达oa wbupload.php,关于通达OA上传附件类型限制的设置

关于上传附件类型限制的设置有些用户在使用OA系统的过程中&#xff0c;要求只允许上传比如后缀名为doc&#xff0c;xls&#xff0c;ppt等的附件文件。如何设置才能实现这一点呢&#xff1f;下面简单介绍下设置方法。1、在该路径MYOA\webroot\inc下找到配置文件oa_config.php文件…

ADC应用

数模转换&#xff08;ADC&#xff09;的应用笔记 智能时代&#xff0c;数字信号已体现在我们生活的方方面面&#xff0c;A/D,D/A是重要的基础。智能手机触摸信号需要转换为数字信号才能分辨触摸位置、数字去抖&#xff1b;打电话或者麦克风需要将模拟声信号转换为数字信号以便存…

springboot 禁用tomcat_Spring Boot 面试的十个问题

点击左上角蓝字&#xff0c;关注“SpringForAll社区”专注分享Spring周边技术内容用下面这些常见的面试问题为下一次 Spring Boot 面试做准备。在本文中&#xff0c;我们将讨论 Spring boot 中最常见的10个面试问题。现在&#xff0c;在就业市场上&#xff0c;这些问题有点棘手…

go 怎么等待所有的协程完成_理解真实世界中 Go 的并发 BUG

点击上方蓝色“Go语言中文网”关注&#xff0c;回复「电子书」领全套Go资料有几个学生研究归纳了go编程中的并发bugs&#xff0c;发表了一篇(英文)论文&#xff1a;《Understanding Real-World Concurrency Bugs in Go》。为你下载好了 PDF&#xff0c;关注公众号 Go语言中文网…

java地图图表动态亮点,可视化图表行动指南:地表最强解读来了

原标题&#xff1a;可视化图表行动指南&#xff1a;地表最强解读来了身处信息技术高速发展的时代&#xff0c;数据价值日益凸显&#xff0c;然而如何将数据更好的展示&#xff0c;让别人一看就懂且眼前一亮可是一门大学问。此前&#xff0c;小亿在广大数友的强烈要求下结合前人…

ASP.NET MVC 使用Swagger需要注意的问题!!!

之前&#xff0c;一直使用的微软自带的Microsoft.AspNet.WebApi.HelpPage来作为项目的接口文档&#xff0c;但总感觉有些不足&#xff0c;就准备采用Swagger。 在项目中引用Swagger很方便&#xff0c;直接Nuget搜索安装Swashbuckle就可以。但是&#xff0c;在使用的过程中&…

分块试水--CODEVS4927 线段树练习5

模板 1 #include<stdio.h>2 #include<algorithm>3 #include<string.h>4 #include<stdlib.h>5 #include<math.h>6 //#include<bitset>7 //#include<iostream>8 using namespace std;9 10 int n,m,q;11 #define maxn 10001112 #define…

【JVM】类的生命周期【转+整理】

参考如下三篇并整理。 1.Java类加载机制详解 2.深入理解Java&#xff1a;类加载机制及反射 3.jvm系列(一):java类的加载机制 类的生命周期是从被加载到虚拟机内存中开始&#xff0c;到卸载出内存结束。过程共有七个阶段。 1.加载---2.验证---3.准备---3.解析---5.初始化---6.使…

java概念,Java基础概念

1. Java编译程序将Java源程序翻译成JVM可执行代码-Java字节码。这一过程同C/C不同。当C编译器翻译成一个对象代码时&#xff0c;该代码是为在某一特定硬件平台运行而产生的。因此&#xff0c;在编译过程中&#xff0c;编译程序通过查表将所有符合引用转换为特定的内存偏移量。而…

【Excle】如何隐藏数据透视表中的错误值

如下&#xff1a;数据透视表出现错误怎么解决呢步骤方法①单击数据透视表任意单元格→数据透视表工具→分析→选项→勾选“对于错误值显示”→确定方法②右键→数据透视表选项&#xff08;同样可以修改&#xff09;转载于:https://www.cnblogs.com/OliverQin/p/8043469.html

repo同步代码_工欲善其事,必先利其器:repo 介绍

介绍此repo非彼repo。这里的repo&#xff0c;是指谷歌公司的一款小工具&#xff0c;名字就叫repo(我承认&#xff0c;这不是一个好名字&#xff0c;Google里面怎么搜都搜不到它的真身)。解决的问题有时候&#xff0c;我们需要在一台电脑上克隆很多个代码仓库&#xff0c;编译它…

ddd 企业应用架构模式_灵魂拷问:用了DDD分包就是落地了领域驱动设计吗?谈谈DDD本质...

学习DDD的时候&#xff0c;作为开发&#xff0c;我们更关心它在技术层面的东西&#xff0c;尤其体现在DDD的分包方式、编码技巧等方面。自然的&#xff0c;我们不禁发问&#xff0c;用了DDD的分包&#xff0c;就是实践落地了DDD了么&#xff1f;不卖关子&#xff0c;直接说答案…

java常见编码

摘自&#xff1a;http://www.cnblogs.com/yaya-yaya/p/5768616.html红色 主要点  灰色 内容 绿色 知识点 橘色 补充内容几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题&#xff0c;那就是为什么要编码&#xff1f;我们能不能不编码&#xff1f;要…

准确率 召回率_机器学习中F值(F-Measure)、准确率(Precision)、召回率(Recall)

在机器学习、数据挖掘、推荐系统完成建模之后&#xff0c;需要对模型的效果做评价。业内目前常常采用的评价指标有准确率(Precision)、召回率(Recall)、F值(F-Measure)等&#xff0c;下图是不同机器学习算法的评价指标。下文讲对其中某些指标做简要介绍。本文针对二元分类器&am…

Saving James Bond - Easy Version 原创 2017年11月23日 13:07:33

06-图2 Saving James Bond - Easy Version&#xff08;25 分&#xff09; This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the worlds most famous spy, was captured by a group of drug dealers. He was sent to …