宠物之家
前言
本项目是一个o2o项目,以宠物为核心,提供宠物领养,宠物收购,宠物寻主,产品购买,服务购买等相关业务。前端用的vue技术栈,后端用的spring,springMVC,springboot,mybatis。
负责模块
用户模块
宠物模块
支付模块
##主要运用技术
1.redis
2.发送短信技术
3.微信三方登录技术
4.md5加密技术
5.RSA加密技术
6.支付宝三方支付技术
7.百度地图定位技术
8.quartz定时器
9.docker
##用户模块
需求:用户注册,用户登录,用户店铺收藏,用户浏览足迹
注册
##表的设计注册方面我们设计了两张表,一张user用户信息表,和loginInfo登录信息表,把用户注册成功后的信息先存放在loginInfo表再存放在user表
后端代码实现
public AjaxResult getVerifyCode(Map<String, String> params) {
String phone = params.get("phone");
//获取前台传来的手机号判断是否为空
if (!StringUtils.hasLength(phone)){
return AjaxResult.ajax().setSuccess(false).setMessage("请输入正确手机号!");
}
//判断手机号是否已经存在
User user = mapper.findOneUser(phone);
if(user!=null){
return AjaxResult.ajax().setSuccess(false).setMessage("该手机号已经被注册");
}
//不存在,就判断redis里面是否有未过期的验证码,从redis获取短信验证码
String key="register"+phone;
String value=(String) redisTemplate.opsForValue().get(key);//code:time
//判断value是否为空
String code="";
//为空
if(!StringUtils.hasLength(value)){
code = StrUtils.getComplexRandomString(4);
}else {
//不为空就判断是否过了重复发短信的时间
long times = Long.valueOf(value.split(":")[1]);
long millis = System.currentTimeMillis();
if(millis-times<1000*60*1){
return AjaxResult.ajax().setSuccess(false).setMessage("请不要重复发送短信");
}else {
code=value.split(":")[0];
}
}
//把value放入redis
redisTemplate.opsForValue().set(key,code+":"+System.currentTimeMillis(),3, TimeUnit.MINUTES);
//发送短信
// Smsutil.sendSms(phone,code);
System.out.println("发送的验证码为:"+code);
return AjaxResult.ajax();
}
登录
设计流程图
##表的设计
在登录方面我们设计了三张表,user用户信息表,employee员工信息表,loginInfo登录信息表,我们把user表和employ表中所有信息都放在了loginInfo表中,并用type字段来进行区分,在前后台登录实现的时候只需要调用一个接口查询一张表
后端实现代码
账号密码登录
public AjaxResult loginInfo(LoginDto loginDto) {
//判断用户信息是否完善
String password1 = loginDto.getPassword();
if (StringUtils.isEmpty(loginDto.getUsername()) || StringUtils.isEmpty(password1) || StringUtils.isEmpty(loginDto.getType())) {
return AjaxResult.ajax().setSuccess(false).setMessage("请输入完整信息");
}
//判断账号是否存在
LoginInfo loginInfo1 = mapper.loadByUsername(loginDto);
if (loginInfo1 == null) {
return AjaxResult.ajax().setSuccess(false).setMessage("账号或密码错误1");
}
//判断该账号是否可用
Integer disable = loginInfo1.getDisable();
if (disable == 0) {
return AjaxResult.ajax().setMessage("该账号不可用").setSuccess(false);
}
//判断密码是否正确
String password = loginInfo1.getPassword();
String salt = loginInfo1.getSalt();
String md5 = MD5Utils.encrypByMd5(password1 + salt);
if (!md5.equals(password)) {
return AjaxResult.ajax().setSuccess(false).setMessage("账号或密码错误2");
//密码正确
} else {
//生成tocken
String tocken = UUID.randomUUID().toString();
template.opsForValue().set(tocken, loginInfo1, 30, TimeUnit.MINUTES);
Map<Object, Object> map = new HashMap<>();
map.put("tocken", tocken);
loginInfo1.setPassword("");
map.put("user", loginInfo1);
String checked = loginDto.getChecked();
map.put("checked", checked);
String pass = loginDto.getPassword();
map.put("pass", pass);
String name = loginDto.getUsername();
map.put("name", name);
return AjaxResult.ajax().setResultObj(map);
}
}
##微信登录
流程图设计
##表的设计
三方登录我们还设计了一张微信用户信息表,用来保存相关的微信用户的信息
实现思路:
前台使用微信提供的接口,需要在其中配置回调页面–
当用户扫码后携带code授权码跳转到回调页面,封装绑定地址,授权码数据发送请求到后台–
后台通过code授权码,发送json请求获取到用户唯一标示(openid)和令牌(accessToken)–
通过openid获取用户信息,有就把用户信息放入redis返回前台,没有就返回绑定页面地址并携带
appenid和accesstoken–
用户在填完绑定信息发请求到后台–
判断用户信息是否填写完善–
通过手机号查询用户,有就通过openid拿到微信用户个人信息存入数据库–
没有,就先注册添加用户信息到loginfo表,user表再拿到微信个人信息存入数据库–
把用户信息放入redis,并返回前台,前台跳转首页
代码实现
//微信登录
public AjaxResult loginWeixin(Map<String, String> params) {
String code = params.get("code");
String binderUrl = params.get("binderUrl");
//通过授权码来获取openid和access_token令牌
String url = WxConstants.GET_ACCESSTOKEN_URL.
replace("APPID", WxConstants.APPID)
.replace("SECRET", WxConstants.SECURITY)
.replace("CODE", code);
String JsonStr = HttpClientUtils.httpGet(url);
JSONObject jsonObject = JSONObject.parseObject(JsonStr);
//拿到openid和令牌access_token
String accesstoken = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
//通过openid拿到user用户
User user = userMapper.getByOpenid(openid);
//判断如果拿到了就说明已经绑定,没拿到就跳转到绑定界面
if (user != null) {
//拿到loginInfo
LoginInfo loginInfo = mapper.loadById(user.getLogininfo_id());
//放入redis
String tocken = UUID.randomUUID().toString();
template.opsForValue().set(tocken, loginInfo, 30, TimeUnit.MINUTES);
HashMap<Object, Object> map = new HashMap<>();
map.put("tocken", tocken);
loginInfo.setPassword("");
map.put("user", loginInfo);
return AjaxResult.ajax().setResultObj(map);
} else {
binderUrl = binderUrl + "?accessToken=" + accesstoken + "&openid=" + openid;
return AjaxResult.ajax().setResultObj(binderUrl).setMessage("binder").setSuccess(false);
}
}
//微信绑定登录
public AjaxResult weixinBinder(LoginDto loginDto) {
String accesstoken = loginDto.getAccesstoken();
String openid = loginDto.getOpenid();
System.out.println(accesstoken );
System.out.println(openid );
//通过accesstoken和openid拿到用户信息
String url = WxConstants.GET_USER_URL
.replace("ACCESS_TOKEN", accesstoken)
.replace("OPENID", openid);
String JsonStr = HttpClientUtils.httpGet(url);
//通过账号判断该用户是否存在,不存在就注册加绑定,存在就绑定
LoginInfo loginInfo = mapper.loadByUsername(loginDto);
if (loginInfo == null) {
loginInfo = loginDtotologinInfo(loginDto);
mapper.insert(loginInfo);
//拿到自增loginInfo的id
User user = UserServiceImpl.LoginInfotoUser(loginInfo);
//返回自增id
userMapper.insert(user);
//拿到user的id放入wxUser的
WxUser wxUser = usertoWxUser(user, JsonStr);
wxUserMapper.save(wxUser);
} else {
//存在就判断密码直接绑定
String salt = loginInfo.getSalt();
String password = loginDto.getPassword();
String md5 = MD5Utils.encrypByMd5(password + salt);
if (!md5.equals(loginInfo.getPassword())) {
return AjaxResult.ajax().setSuccess(false).setMessage("账号或密码错误");
}
String username = loginInfo.getUsername();
User user = userMapper.findOneUser(username);
WxUser wxUser = usertoWxUser(user, JsonStr);
wxUserMapper.save(wxUser);
}
//放入redis
String tocken = UUID.randomUUID().toString();
template.opsForValue().set(tocken, loginInfo, 30, TimeUnit.MINUTES);
HashMap<Object, Object> map = new HashMap<>();
map.put("tocken", tocken);
loginInfo.setPassword("");
map.put("user", loginInfo);
return AjaxResult.ajax().setResultObj(map);
}
其他收藏,足迹
宠物模块
##表的设计
宠物模块我们一共设计了四张表,宠物信息表,宠物表,宠物详情表,
宠物类型表
实现思路:
寻主:
用户发布寻主消息,存放寻主消息,把消息发送给最近的一家店铺,
店铺处理生成相关宠物信息以及收购订单。
领养:用户点击领养,后台生成订单,支付单。
后台代码:
@Override
public void save(SeachMasterMsg msg, LoginInfo loginInfo) {
//通过loginInfoid拿到user对象;
User user = mapper.getById(loginInfo.getId());
//拿到userId放入SeachMasterMsg
msg.setUser_id(user.getId());
//拿到用户地址经纬度
Point point = DistanceUtil.getPoint(msg.getAddress());
//拿到所有店铺地址
List<Shop> shops = shopMapper.findAll();
Shop nearestShop = DistanceUtil.getNearestShop(point, shops);
if (nearestShop==null){
throw new RuntimeException("抱歉附近没有门店!") ;
}
//拿到最近的shop放入SeachMasterMsg,并给店铺管理员发信息
msg.setShop_id(nearestShop.getId());
Smsutil.sendSms(nearestShop.getAdmin().getPhone(),"老板附近有宠物可领取哦!" );
msgMapper.save(msg);
}
@Override//将信息转换成宠物
public void insert(Pet pet,LoginInfo loginIonf) {
//将消息改为已处理
Long id = pet.getSeachmastermsg_id();
SeachMasterMsg seachMasterMsg = msgMapper.loadById(id);
seachMasterMsg.setState(1);
msgMapper.update(seachMasterMsg);
//保存宠物信息(处理后的信息前台传来)
petService.insert(pet);
//生成订单
PetAcquisitionOrder petAcquisitionOrder = petto2order(pet,loginIonf,seachMasterMsg);
petAcquisitionOrderMapper.save(petAcquisitionOrder);
}
@Override
public void updateAdopt(Long id, LoginInfo loginInfo) {
//拿到用户确认是谁领养的
User user = userMapper.getById(loginInfo.getId());
Pet pet = petMapper.loadById(id);
pet.setUser_id(user.getId());
//修改为被领养状态
pet.setState(2);
petMapper.update(pet);
}
其他 宠物详情的展示crud
支付模块(支付宝支付为例)
支付宝流程
##表的设计
对于这个业务我们设计了三张表,支付表,支付流水表,支付宝支付信息表
实现思路:
后台封装了支付宝提供的接口–
当用户交订单就会生成支付订单,然后调用支付接口–
需要传入支付宝公钥,商家的私钥,统一支付单号,支付价格等信息–
支付宝返回一个支付数据包,把数据包返回前台–
前台解析数据包,拿到form表单,界面弹出支付二维码–
用户扫码之后同步回调到我们指定的页面,并异步回调后台指定的接口–
在接口修改相关订单及支付单的状态,并添加支付流水
设置定时器30分钟未支付就取消订单
实现代码
public AjaxResult productOrder(LoginInfo loginInfo, OrderDto dto) {
Long id = dto.getAddress_id();
UserAddress userAddress = userAddressMapper.loadById(id);
//添加主表ProductOrder
ProductOrder productOrder = OrderDto2ProductOrder(loginInfo,dto,userAddress);
String paySn = CodeGenerateUtils.generateUnionPaySn();
productOrder.setPaySn(paySn);
productOrderMapper.save(productOrder);
//添加到其他关联表,订单地址表
OrderAddress orderAddress=userAddressto2orderAddress(userAddress,productOrder);
orderAddressMapper.save(orderAddress);
//订单详情表
Long product_id = dto.getProduct_id();
Product product = productMapper.loadById(product_id);
ProductOrderDetail productOrderDetail = productto2productDetail(product,productOrder);
productOrderDetailMapper.save(productOrderDetail);
//生成支付表单;
PayBill payBill = productOrderto2payBill(productOrder,dto);
payBill.setUnionPaySn(paySn);
//保存单据
payBillMapper.save(payBill);
//返回一个支付宝数据包会form表单返回给前台
String result = payBillService.pay(payBill);
//设置订单超时取消
QuartzJobInfo info = new QuartzJobInfo();
info.setType(JobContstants.PRODUCT_ORDER_CANCEL);
info.setJobName("product_order_cancel_"+productOrder.getId());
//会调用set方法转成conj形式
info.setFireDate(productOrder.getLastPayTime());
HashMap<String, Object> map = new HashMap<>();
map.put("orderId", productOrder.getId());
info.setParams(map);
quartzService.addObj(info);
return AjaxResult.ajax().setResultObj(result);
}
支付宝工具类
public static String aliPay(AlipayInfo alipayInfo, PayBill payBill){
try {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(Alipayconfig.gatewayUrl,alipayInfo.getAppid(), alipayInfo.getMerchant_private_key(), "json", Alipayconfig.charset, alipayInfo.getAlipay_public_key(), Alipayconfig.sign_type);
//设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(Alipayconfig.return_url);
alipayRequest.setNotifyUrl(Alipayconfig.notify_url);
//商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = new String(payBill.getUnionPaySn());
//付款金额,必填
String total_amount = new String(payBill.getMoney().toString());
//订单名称,必填
String subject = new String(payBill.getDigest());
//商品描述,可空
String body = new String("服务订单");
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求
String result = alipayClient.pageExecute(alipayRequest).getBody();
return result;
//输出
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*修改日期:2017-04-05
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://syupi2.natappfree.cc/notify";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "http://localhost/success.html";
// 支付宝网关
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
public static String sign_type = "RSA2";
public static String charset = "utf-8";
微信,余额支付实现思路差不多