宠物乐园是我最近做的一个O2O项目,支持商家入驻,为线下门店提供客源,从中抽取分成,实现互利共赢。
随着生活水平的提高,人们对精神方面的需求也不断提高,很多人喜欢通过喂养宠物来丰富自己的精神需求,于是公司就研发了宠物乐园这个项目,这个项目主要功能:用户登录此平台可以领养宠物,平台的宠物来源主要分为两种,一是基地饲养,二是用户没精力喂养宠物时可以通过该平台发布宠物相关信息,平台就近分配给附近的线下门店,门店安排员工上门进行回收。回收后进行打理并训练后上架。
用户可以在线上为自己的宠物购买服务(上门洗澡,美容,寄养等),用户下单后就近分配给附近的线下门店。
项目前端使用vue及live_server后端使用springboot_mybatis使用的数据库是mysql和redis
项目分为7个模块组织机构管理模块,用户模块,服务模块,宠物模块,订单模块,支付模块,定时任务模块,其中我做了宠物模块、订单模块及支付模块
宠物模块的主要包括寻主和领养:
寻主:用户登录平台后点击发布填写自己不想继续喂养的宠物信息进行发布,平台收到信息就近分配给入驻商家, 商家员工上门进行回收,回收资金由员工垫付后报账,收回宠物后处理寻主信息将寻主信息状态修改为已处理,并将宠物信息完善后加入宠物表中。
商家可以对自己的宠物进行上下架。
用户在主站上领养已经上架的宠物,点击领养后生成宠物订单,订单地址和支付订单并调用支付方法进行支付,并设置30分钟取消的定时任务
二。负责业务模块
宠物模块
支付模块
三。项目人员组成
项目经理(PM):管人管项目 1
架构师(SE): 负责项目架构+技术选型+疑难问题解决+培训 1
UI: 设计界面 1
H5: 前端开发工程师 2
后台: 后台开发工程师 3-4‘、
测试: 测试人员 1
运维人员: 搭建开发公共环境,线上环境 1
四。项目架构 前后端分离
前后端分离并不只是开发模式,而是web应用的一种架构模式。在开发阶段,前后端工程师约定好数据交互接口,实现并行架构,开发和测试;在运行阶段前后端分离模式需要对web应用进行分别部署,前后端之间使用HTTP或者其他协议进行交互请求。
前后端分离的优缺点:
优点:
分工明确,专业的人做专业的事情。
专业可以做出炫丽优美界面
人员培养更加专业化,精确化
缺点:
成本提高,人员多。
项目管理难度也提高,前后端都要管,协调他们之间交流。
五。使用的技术
Springboot 后端框架FastDFS 分布式文件系统 Redis 一种非关系型的数据库短信消息 百度地图 微信三方登录 支付宝支付加密技术邮件技术QuartzVue技术栈: nodejs npm webpack vuecli elmentui easymockLinuxdocker六。功能实现
寻主消息
宠物类型
宠物
宠物详情
领养
支付宝支付
七。跨域解决
1:后端配置-最全 最通用,开发的时候我们选择它
2:前端配置-只适合脚手架搭建项目(vuecli),不支持纯html+css(主站)
3:Nginx代理 上线的时候就使用它
redis是一款优秀的nosql(非关系型),key-vlaue,内存级别的数据库!redis是单线程,操作是安全的。
Redis的list可以用来实现消息队列,key用来表示队列名称,客户端向对应的key代表的队列发消息,发消息采用rpush,lpush实现,取消息采用rpop或lpop实现。
Redis存储机制分成两种
RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。
无论是那种机制,Redis都是将数据存储在内存中。
redis的淘汰策略
淘汰一些数据,达到redis数据都是有效的,节约内存资源。选择合适的淘汰策略进行淘汰。
FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
用在店铺入驻上传logo的时候
原来的操作都需要对数据库crud
为了减轻服务器压力。
前端怎么独立开发?
我们采用的是Element,element是一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库,提供了很多的ui组件。我们可以使用这些组件快速开发前端项目。
swagger是什么?
swagger是接口规范。接口测试人员和前段开发人员要通过接口描述测试接口,所以需要定义一个接口规范。
swagger可以直接罗列所有接口,每个接口有访问地址(访问方式),参数及返回值.可以直接通过后端代码产生能够让前台开发或测试人员能够看懂的接口文档。
交互方式
在前后端分离架构中,后端只需要负责按照约定的数据格式向前端提供可调用的API服务即可。前后端之间通过HTTP请求进行交互,前端获取到数据后,进行页面的组装和渲染,页面跳转,最终返回给浏览器。
后端如何进行接口测试?
采用了一个postman工具。postman就是一个http协议接口测试工具,可以来发送各种http请求,可以用它来测试http协议接口.
//文件上传工具类封装
package org.chengang.basic.util;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
public class FastDfsUtil {
//从classpath
public static String CONF_FILENAME = FastDfsUtil.class.getClassLoader()
.getResource("fdfs_client.conf").getFile();
/**
public static String upload(byte[] file,String extName) {
try {
ClientGlobal.init(CONF_FILENAME);
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
NameValuePair nvp [] = new NameValuePair[]{
new NameValuePair("age", "18"),
new NameValuePair("sex", "male")
};
String fileIds[] = storageClient.upload_file(file,extName,nvp);
System.out.println(fileIds.length);
System.out.println("组名:" + fileIds[0]);
System.out.println("路径: " + fileIds[1]);
return "/"+fileIds[0]+"/"+fileIds[1];
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
public static String upload(String path,String extName) {
try {
ClientGlobal.init(CONF_FILENAME);
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
String fileIds[] = storageClient.upload_file(path, extName,null);
System.out.println(fileIds.length);
System.out.println("组名:" + fileIds[0]);
System.out.println("路径: " + fileIds[1]);
return "/"+fileIds[0]+"/"+fileIds[1];
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
public static byte[] download(String groupName,String fileName) {
try {
ClientGlobal.init(CONF_FILENAME);
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
byte[] b = storageClient.download_file(groupName, fileName);
return b;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
public static void delete(String groupName,String fileName){
try {
ClientGlobal.init(CONF_FILENAME);
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer,
storageServer);
int i = storageClient.delete_file(groupName,fileName);
System.out.println( i==0 ? "删除成功" : "删除失败:"+i);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("删除异常,"+e.getMessage());
}
}
}
//fastdfs配置文件
fdfs_client.conf
tracker_server=122.51.119.246:22122
package org.chengang.basic.util;
import java.io.UnsupportedEncodingException;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.chengang.basic.constant.MsgConstant;
public class SendMsg_webchinese {
private static PostMethod post=null;
public static String sendMsg(String phone,String content) {
try {
HttpClient client = new HttpClient();
post = new PostMethod("http://utf8.api.smschinese.cn/");
post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf8");
NameValuePair[] data = {new NameValuePair("Uid", MsgConstant.UID), new NameValuePair("Key", MsgConstant.KEY), new NameValuePair("smsMob", phone), new NameValuePair("smsText", content)};
post.setRequestBody(data);
client.executeMethod(post);
Header[] headers = post.getResponseHeaders();
int statusCode = post.getStatusCode();
System.out.println("statusCode:" + statusCode);
for (Header h : headers) {
System.out.println(h.toString());
}
String result = new String(post.getResponseBodyAsString().getBytes("utf8"));
System.out.println(result);
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}finally {
post.releaseConnection();
}
}
}
package org.chengang.basic.util;
import org.chengang.basic.domain.Point;
import org.chengang.org.domain.Shop;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
public class DistanceUtil {
public static Point getPoint(String address){
String Application_ID="msGZMt2RFGa0V9InVgYytSmivbQGLafs";
try{
String sCurrentLine; String sTotalString;sCurrentLine ="";
sTotalString = "";
InputStream l_urlStream;
URL l_url = new URL("http://api.map.baidu.com/geocoding/v3/?address="+address+"&output=json&ak="+Application_ID+"&callback=showLocation");
HttpURLConnection l_connection = (HttpURLConnection) l_url.openConnection();
l_connection.connect();
l_urlStream = l_connection.getInputStream();
java.io.BufferedReader l_reader = new java.io.BufferedReader(new InputStreamReader(l_urlStream));
String str=l_reader.readLine();
System.out.println(str);
String s=","+"""+"lat"+"""+":";
String strs[]=str.split(s,2);
String s1="""+"lng"+"""+":";
String a[]=strs[0].split(s1, 2);
s1="}"+","+""";
String a1[]=strs[1].split(s1,2);
Point point=new Point();
point.setLng(Double.valueOf(a[1]));
point.setLat(Double.valueOf(a1[0]));
return point;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
System.out.println(getPoint("成都"));
}
private static final double EARTH_RADIUS = 6378137;
private static double rad(double d)
{
return d * Math.PI / 180.0;
}
public static double getDistance(Point point1,Point point2)
{
double radLat1 = rad(point1.getLat());
double radLat2 = rad(point2.getLat());
double a = radLat1 - radLat2;
double b = rad(point1.getLng()) - rad(point2.getLng());
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
s = s * EARTH_RADIUS;
s = Math.round(s * 10000) / 10000;
return s;
}
public static Shop getNearestShop (Point point, List<Shop> shops) {
Shop shop=shops.get(0);
double distance=getDistance(point,getPoint(shops.get(0).getAddress()));
if (shops.size()>1){
for (int i=1;i<shops.size();i++){
if (getDistance(point,getPoint(shops.get(i).getAddress()))<distance){
shop=shops.get(i);
}
}
}
return shop;
}
}
支付宝支付工具类封装
package org.chengang.pay.utils;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import org.chengang.pay.constants.AlipayConfig;
import org.chengang.pay.domain.AlipayInfo;
import org.chengang.pay.domain.PayBill;
public class AlipayUtils {
/**
public static String pay(AlipayInfo info, PayBill payBill){
try {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, info.getAppid(), info.getMerchant_private_key(),
"json", AlipayConfig.charset, info.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;
}
}
支付宝支付配置文件
package org.chengang.pay.constants;
/* *
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
public static String alipay_public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB";
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://ust4fs.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";
}
支付宝支付工具类封装
package org.chengang.pay.utils;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import org.chengang.pay.constants.AlipayConfig;
import org.chengang.pay.domain.AlipayInfo;
import org.chengang.pay.domain.PayBill;
public class AlipayUtils {
/**
public static String pay(AlipayInfo info, PayBill payBill){
try {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, info.getAppid(), info.getMerchant_private_key(),
"json", AlipayConfig.charset, info.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;
}
}
支付宝回调接口
package org.chengang.pay.controller;
import com.alipay.api.internal.util.AlipaySignature;
import org.chengang.order.domain.ProductOrder;
import org.chengang.order.service.IProductOrderService;
import org.chengang.pay.constants.AlipayConfig;
import org.chengang.pay.constants.PayConstant;
import org.chengang.pay.domain.AlipayInfo;
import org.chengang.pay.domain.PayBill;
import org.chengang.pay.service.IAlipayInfoService;
import org.chengang.pay.service.IPayBillService;
import org.chengang.quartz.JobContstants;
import org.chengang.quartz.service.IQuartzService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@RestController
public class AlipayController {
@Autowired
private IPayBillService payBillService;
@Autowired
private IAlipayInfoService alipayInfoService;
@Autowired
private IProductOrderService productOrderService;
@Autowired
private IQuartzService quartzService;
//回调方法
@PostMapping("/notify")
public String notifyMethod(HttpServletRequest request){
try {
//获取支付宝POST过来反馈信息
Map<String,String> params = new HashMap<String,String>();
Map<String,String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用
valueStr = new String(valueStr);
params.put(name, valueStr);
}
// System.out.println(params);
// for (String key : params.keySet()) {
// System.out.println(key);
// System.out.println(params.get(key));
// }
//out_trade_no-->unionPaySn
String unionPaySn = params.get("out_trade_no");
PayBill payBill = payBillService.queryPayBillByUnionPaySn(unionPaySn);
Long shopId = payBill.getShop_id();
AlipayInfo info = alipayInfoService.getByShopId(shopId);
boolean signVerified = AlipaySignature.rsaCheckV1(params, info.getAlipay_public_key(), AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名
//——请在这里编写您的程序(以下代码仅作参考)——
/* 实际验证过程建议商户务必添加以下校验:
1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
if(signVerified) {//验证成功
//商户订单号
String out_trade_no = request.getParameter("out_trade_no");
//支付宝交易号
String trade_no = request.getParameter("trade_no");
//交易状态
String trade_status = request.getParameter("trade_status");
if(trade_status.equals("TRADE_FINISHED")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
}else if (trade_status.equals("TRADE_SUCCESS")){ //支付成功
//修改支付单状态
payBill.setState(1);
payBillService.update(payBill);
//修改订单状态
String businessType = payBill.getBusinessType();
Long businessKey = payBill.getBusinessKey();
if(PayConstant.BUSINESS_TYPE_PRODUCT_ORDER.equals(businessType)){
//服务订单
ProductOrder productOrder = productOrderService.getById(businessKey);
productOrder.setState(1);
productOrderService.update(productOrder);
quartzService.deleteJob("product_order_cancel_"+productOrder.getId());
return "success";
//支付成功删除定时任务
}
}
}else {//验证失败
//调试用,写文本函数记录程序运行情况是否正常
//String sWord = AlipaySignature.getSignCheckContentV1(params);
//AlipayConfig.logResult(sWord);
}
//——请在这里编写您的程序(以上代码仅作参考)——
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
相关知识
宠物之家app最新版下载
宠物管理之家
萌宠宠物之家App下载
宠物之家交流平台的设计与实现 毕业设计开题报告
现代童话:流浪狗之家 Hotel for Dogs
宠物训练。四川省成都市happy baby 灵宠之家幼儿园重
宠物之家慈善设计大赛公开征集
郑州市惠济区萌宠之家宠物生活馆招聘
打造温馨动物之家:揭秘动物门诊装修效果图
天津宠之家宠物美容服务有限公司
网址: 宠物管理之家 https://m.mcbbbk.com/newsview23962.html
上一篇: 漂流岛博客宠物 |
下一篇: 关于养宠物的感慨【熊猫兔吧】 |