目录
业务介绍
业务概述
用户发布寻主消息到平台,平台收到消息后匹配最近门店去上门收购,然后可以上架发布售卖,
发布消息,展示消息,处理消息
发布,收购,上架,领养
百度地图技术:地址关键字自动匹配补全,
需求分析与流程设计中一般不同的名词对应着不同的表,要了解表的字段和能完成的功能
1 用户发布寻主消息
2 平台接收消息
3 找到最近门店,发送短信消息给门店管理员,并且把这个寻主消息划归他门下
4 收取宠物,如果要钱的,还要创建订单并完成支付
名词
寻主消息,宠物类型,宠物,宠物详情,寻主订单(服务订单,商品订单,充值订单等)
表设计
寻主消息表
sql
CREATE TABLE `t_search_master_msg` (
`id` bigint(10) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL COMMENT '宠物名称',
`age` int(10) DEFAULT NULL,
`gender` tinyint(1) DEFAULT NULL,
`coat_color` varchar(255) DEFAULT NULL COMMENT '毛色',
`resources` varchar(255) DEFAULT NULL,
`pet_type` bigint(255) DEFAULT NULL COMMENT '类型',
`price` double(10,3) DEFAULT NULL,
`address` varchar(255) DEFAULT '',
`state` int(11) DEFAULT '0' COMMENT '待处理 0 已处理1',
`title` varchar(255) DEFAULT NULL,
`user_id` bigint(255) DEFAULT NULL,
`shop_id` bigint(20) DEFAULT NULL COMMENT '店铺id 消息分配的店铺',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=315 DEFAULT CHARSET=utf8;
宠物类型表
sql
CREATE TABLE `t_pet_type` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`description` text,
`pid` bigint(20) DEFAULT NULL COMMENT '父类型id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
宠物表,search_master_msg_id要允许为null
sql
CREATE TABLE `t_pet` (
`id` bigint(2) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`saleprice` decimal(19,2) DEFAULT NULL,
`costprice` varchar(255) DEFAULT NULL,
`resources` varchar(255) DEFAULT NULL,
`state` bigint(2) DEFAULT NULL COMMENT '状态:0下架 1上架',
`type_id` bigint(20) DEFAULT NULL COMMENT '类型id',
`offsaletime` datetime DEFAULT NULL,
`onsaletime` datetime DEFAULT NULL,
`createtime` datetime DEFAULT NULL,
`shop_id` bigint(20) DEFAULT NULL COMMENT '店铺Id 如果被领养店铺id为null',
`user_id` bigint(20) DEFAULT NULL COMMENT '如果被领养为领养用户id',
`search_master_msg_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8;
宠物详情表
sql
CREATE TABLE `t_pet_detail` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`pet_id` bigint(20) DEFAULT NULL,
`adoptNotice` text COMMENT '领养须知',
`intro` text COMMENT '简介',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8;
宠物模块搭建
java,xml,yml
发布寻主消息
前台
拷贝publish页面到ph-web根目录,从我的福利开新窗体跳转
html
<div class="nav-extra" @click="goPublish">
<i class="am-icon-user-secret am-icon-md nav-user" @></i><b></b>发布寻主消息
<i class="am-icon-angle-right" style="padding-left: 10px;"></i>
</div>
js
methods:{
goPublish(){//跳转到发布寻主消息页面
window.open("publish.html");
}
},
publish.html修改引入路径,删除最后一个无关的div
后端
百度地图技术
概述:
百度地图开发平台:http://lbsyun.baidu.com/
控制台 - 应用管理 - 我的应用 - 创建应用 应用名称:pethome 应用类型:浏览器端 Referer白名单:*(即所有域名皆可访问)
逆地址解析示例:开发文档 - JavaScript API - 示例DEMO - 路线规划 - 步行规划 - 根据起终点名称规划步行线路
检索服务 - 输入提示示例 - 关键字提示输入 - 输入地址可匹配相关地址
应用:
拷贝百度关键字提示输入代码到ph-web根目录mapTest.html,换上自己的秘钥
1.publish引入百度地图,
html
<!--引入百度地图-->
<script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=h7cuNiHW3f2Sz1OsCEbzCDfkUEmwLHNA"></script>
2.拷贝整个百度地图js到publish,(取消初始定位)
见工程
0.绑定表单id(已对齐),
html
<!--private String address;-->
<div class="am-form-group">
<label for="suggestId" class="am-form-label">地址</label>
<div class="am-form-content">
<input type="text" id="suggestId" v-model="searchMasterMsg.address" placeholder="请输入地址">
</div>
</div>
js
var ac = new BMap.Autocomplete( //建立一个自动完成的对象
{"input" : "suggestId"
,"location" : map
});
4.将返回的结果集赋值给address,
js
var myValue;
ac.addEventListener("onconfirm", function(e) { //鼠标点击下拉列表后的事件
var _value = e.item.value;
myValue = _value.province + _value.city + _value.district + _value.street + _value.business;
G("searchResultPanel").innerHTML ="onconfirm<br />index = " + e.item.index + "<br />myValue = " + myValue;
vue.searchMasterMsg.address = myValue;
setPlace();
});
拷贝DistanceUtil和point
Point
java
/**
* 存放经纬度
*/
@Data
public class Point {
//经度
private Double lng;
//维度
private Double lat;
}
DistanceUtil
java
package cn.ming;
import cn.ming.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="h7cuNiHW3f2Sz1OsCEbzCDfkUEmwLHNA";//配置上自己的百度地图应用的AK
try{
String sCurrentLine; String sTotalString;sCurrentLine ="";
sTotalString = "";
InputStream l_urlStream;
// URL l_url = new java.net.URL("http://api.map.baidu.com/geocoder/v2/?address="+address.replaceAll(" ","")+"&output=json&ak="+Application_ID+"&callback=showLocation");
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;
}
}
//地球半径,进行经纬度运算需要用到的数据之一
private static final double EARTH_RADIUS = 6378137;
//根据坐标点获取弧度
private static double rad(double d)
{
return d * Math.PI / 180.0;
}
/**
* 根据两点间经纬度坐标(double值),计算两点间距离,单位为米
* @param point1 A点坐标
* @param point2 B点坐标
* @return
*/
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;
}
/**
* 根据两点间经纬度坐标(double值),计算两点间距离,单位为米
* @param point 用户指定的地址坐标
* @param shops 商店
* @return
*/
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;
}
public static void main(String[] args) {
System.out.println(getPoint("成都市武侯区天府新谷-10号楼"));
}
}
SearchMasterMsgController
java
/**
* 发布寻主消息
*/
@PostMapping("/publish")
public AjaxResult publish(@RequestBody SearchMasterMsg searchMasterMsg, HttpServletRequest request){
try {
Logininfo loginInfo = LoginContext.getLoginIn(request);
seachMasterMsgService.publish(searchMasterMsg, loginInfo.getId());
return AjaxResult.me();
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.me().setMessage("发布失败!"+e.getMessage());
}
}
SearchMasterMsgServiceImpl
java
@Override
@Transactional
public void publish(SearchMasterMsg searchMasterMsg, Long loginInfoId) {
//1.找到最近的门店 绑定门店到寻主消息
// 1.1用地址计算发布寻主消息人员的经纬度
Point point = DistanceUtil.getPoint(searchMasterMsg.getAddress());
//1.2 根据点计算出最近的门店
Shop nearestShop = DistanceUtil.getNearestShop(point, shopMapper.loadAll());
searchMasterMsg.setShop(nearestShop);
searchMasterMsg.setShop_id(nearestShop.getId());
//2.绑定谁发的消息(user)
User user = userMapper.loadByloginInfoId(loginInfoId);
searchMasterMsg.setUser(user);
searchMasterMsg.setUser_id(user.getId());
//3.存储寻主消息
searchMasterMsgMapper.save(searchMasterMsg);
// 4. 发送消息通知店铺管理员
//SmsUtils.sendSms(nearestShop.getTel(), "老板,你有【"+searchMasterMsg.getName()+"】,快去接客!");
System.out.println("老板,你有【"+searchMasterMsg.getName()+"】消息,快去接客!");
}
消息查看
思路
简单做:用户可查,平台管理员可查,店铺人员可查(店铺管理员 店铺员工都可以),分作3个接口
待处理消息和已处理消息两个页面
待处理消息:平台管理员可看全部,被分配的店铺人员可看自己的
后台查看:
employee的shopid属于某店铺,则这些employee都可查自己店铺的所有消息 - 给后台用
message里没有user也没有shop,没有分配的平台管理员都可以看 - 给后台用
用户只能查看自己订单 user_id - 给前台用
平台+用户+已处理
待处理消息接口(平台人员看全部+店铺人员看自己),
已处理消息接口(平台人员看全部+店铺人员看自己),
用户看自己消息的接口(自己的待处理消息+已处理消息)
后端
SearchMasterMsgController
java
/**
* 待处理消息查询接口,平台管理员 + 店铺员工(店铺管理员 + 员工)
* 待处理消息查询接口,平台管理员和店铺人员都可以进来查,根据是否存在shopid分别查到各自可以看的数据
*/
@PostMapping("/pending")
public PageList<SearchMasterMsg> pending(@RequestBody SearchMasterMsgQuery query, HttpServletRequest request){
try {
Logininfo loginInfo = LoginContext.getLoginIn(request);
return seachMasterMsgService.pending(query, loginInfo.getId());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 已处理消息查询接口,平台管理 + 店铺员工(店铺管理员 + 员工)
* 已处理消息查询接口,同上,只是类型参数不一样
*/
@PostMapping("/processed")
public PageList<SearchMasterMsg> processed(@RequestBody SearchMasterMsgQuery query, HttpServletRequest request){
try {
Logininfo loginInfo = LoginContext.getLoginIn(request);
return seachMasterMsgService.processed(query, loginInfo.getId());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 前端用户查询自己消息的接口
* 前端用户查询自己消息的接口,已处理待处理都能查
*/
@PostMapping("/user")
public PageList<SearchMasterMsg> queryUser(@RequestBody SearchMasterMsgQuery query, HttpServletRequest request){
try {
Logininfo loginInfo = LoginContext.getLoginIn(request);
return seachMasterMsgService.queryUser(query, loginInfo.getId());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
SearchMasterMsgServiceImpl
java
//待处理消息查询接口,平台管理员和店铺人员都可以进来查,根据是否存在shopid分别查到各自可以看的数据
@Override
public PageList<SearchMasterMsg> pending(SearchMasterMsgQuery query, Long loginInfoId) {
query.setState(0); //SearchMasterMsgQuery state 0待处理 1已处理
//1.通过loginInfoID查询出Employee
Employee employee = employeeMapper.loadByLoginInfoId(loginInfoId);
//2.如果employee中的shopID不为null,就是店铺工作人员。否则就是平台员工
if(employee.getShop_id() != null){
query.setShopId(employee.getShop_id());//把员工的shopid放到分页查询条件里 SearchMasterMsgQuery shopId
}
return super.queryPage(query); //SearchMasterMsgQuery 返回查询结果
}
//已处理消息查询接口,同上,只是类型参数不一样
@Override
public PageList<SearchMasterMsg> processed(SearchMasterMsgQuery query, Long loginInfoId) {
query.setState(1);
//1.通过loginInfoID查询出Employee
Employee employee = employeeMapper.loadByLoginInfoId(loginInfoId);
//2.如果employee中的shopID不为null,就是店铺。否则就是平台员工
if(employee.getShop_id() != null){
query.setShopId(employee.getShop_id()); //因为shopId如果有值,就是查询门店的数据
}
return super.queryPage(query);
}
//前端用户查询自己消息的接口,已处理待处理都能查
@Override
public PageList<SearchMasterMsg> queryUser(SearchMasterMsgQuery query, Long loginInfoId) {
User user = userMapper.loadByloingInfoId(loginInfoId);
query.setUserId(user.getId()); //因为UserId如果有值,查询用户
return super.queryPage(query);
}
SearchMasterMsgMapper
xml
<sql id="whereSql">
<where>
<if test="state!=null">
and a.state = #{state}
</if>
<if test="userId!=null">
and a.user_id = #{userId}
</if>
<if test="shopId!=null">
and a.shop_id = #{shopId}
</if>
</where>
</sql>
<!--//查询总条数-->
<!--Long queryCount(BaseQuery query);-->
<select id="queryCount" parameterType="SearchMasterMsgQuery" resultType="integer">
select count(*) from t_search_master_msg a
<include refid="whereSql"/>
</select>
<!--//查询数据-->
<!--List<T> queryData(BaseQuery query);-->
<select id="queryData" parameterType="SearchMasterMsgQuery" resultMap="SearchMasterMsgMap">
SELECT
a.*, u.id uid,
u.username,
s.id sid,
s. NAME sname,
t.id tid,
t.name tname
FROM
t_search_master_msg a
LEFT JOIN t_user u ON u.id = a.user_id
LEFT JOIN t_shop s ON s.id = a.shop_id
LEFT JOIN t_pet_type t on t.id = a.pet_type
<include refid="whereSql"/>
limit #{start},#{pageSize}
</select>
后台
页面准备
拷贝SearchMasterMsgPending.vue和SearchMasterMsgProcessed.vue到ph-admin的pet包下
配置路由
构造数据后即可测试
消息处理
点击处理
后台
略
后端
SearchMasterMsgController
java
/**
* 处理消息
*/
@PutMapping("/handle")
public AjaxResult handle(@RequestBody Pet pet){
try {
seachMasterMsgService.handle(pet);
return AjaxResult.me();
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.me().setMessage("处理失败!"+e.getMessage());
}
}
SearchMasterMsgServiceImpl
java
/**
* 处理消息
*/
@Override
public void handle(Pet pet) {
//1.改状态 --已处理
searchMasterMsgMapper.updateStateForProcessed(pet.getSearch_master_msg_id());
//2.生成宠物基本信息
petMapper.save(pet);
//3.宠物详情
PetDetail detail = pet.getDetail();
if(detail != null){
detail.setPet_id(pet.getId());
petDetailMapper.save(detail);
}
//4.生成订单@TODO
//5.生成支付@TODO
}
SearchMasterMsgMapper
xml
<!--void updateStateForProcessed(Long search_master_msg_id);-->
<update id="updateStateForProcessed" parameterType="long">
UPDATE t_search_master_msg set state = 1 WHERE id = #{search_master_msg_id}
</update>
后台完成
略