Appearance
Session功能实现
1.1业务流程图
1.2接口定义
json
#Request:
method: GET
url:api/v1.0/session
data:no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg":"OK",
"data": {"name" : "13313331333"}
}
#注册失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
1.3具体业务实现
session的处理,我们可以直接放在web端来实现,具体实现如下:
go
//定义返回数据容器
resp := make(map[string]interface{})
dataTmp := make(map[string]string)
//初始化session
s := sessions.Default(ctx)
userName := s.Get("userName")
//对获取的session校验
if userName == nil{
//初始化返回值
resp["errno"] = utils.RECODE_SESSIONERR
resp["errmsg"] = utils.RecodeText(utils.RECODE_SESSIONERR)
}else{
resp["errno"] = utils.RECODE_OK
resp["errmsg"] = utils.RecodeText(utils.RECODE_OK)
dataTmp["name"] = userName.(string)
resp["data"] = dataTmp
}
ctx.JSON(200,resp)
2.用户信息展示
2.1业务流程图
2.2接口定义
json
#Request:
method: GET
url:api/v1.0/user
#data:
no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"user_id": 1,
"name": "itcast",
"mobile": "110",
"real_name": "传智",
"id_card": "210112244556677",
"avatar_url": "http://101.200.170.171:8888/group1/M00/00/00/Zciqq1n7It2ANn1dAADexS5wJKs808.png"
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
2.3服务端实现
首先我们根据接口定义来确定proto文件的参数,修改后的proto文件如下:
protobuf
syntax = "proto3";
package go.micro.srv.getUserInfo;
service GetUserInfo {
rpc Call(Request) returns (Response) {}
}
message Request {
string name = 1;
}
message Response {
string errno = 1;
string errmsg = 2;
UserData data = 3;
}
message UserData{
int32 user_id = 1;
string name = 2;
string mobile = 3;
string real_name = 4;
string id_card = 5;
string avatar_url = 6;
}
然后修改main.go文件,再详细的实现我们的业务方法,具体代码如下:
go
//根据用户名获取信息
db ,err:= model.InitDb()
user,err := model.GetUserInfo(db,req.Name)
if err != nil {
rsp.Errno = utils.RECODE_DBERR
rsp.Errmsg = utils.RecodeText(utils.RECODE_DBERR)
return err
}
var data getUserInfo.UserData
data.Name = user.Name
data.Mobile = user.Mobile
data.IdCard = user.Id_card
data.RealName = user.Real_name
data.AvatarUrl = user.Avatar_url
data.UserId = int32(user.ID)
rsp.Errno = utils.RECODE_OK
rsp.Errmsg = utils.RecodeText(utils.RECODE_OK)
rsp.Data = &data
return nil
注意函数的封装,封装的函数如下:
go
//根据用户名获取当前用户信息
func GetUserInfo(db*gorm.DB,name string)(User,error){
var user User
err := db.Where("name = ?",name).First(&user).Error
return user,err
}
2.4web端实现
服务端实现之后,接着我们来实现web端,首先我们要匹配相应的路由,代码如下:
go
r1.GET("/user",controller.GetUserInfo)
然后实现具体的控制器函数,首先是从session中获取数据,代码如下:
go
//获取登录信息
s := sessions.Default(ctx)
userName := s.Get("userName")
errResp := make(map[string]interface{})
if userName == nil{
errResp["errno"] = utils.RECODE_SESSIONERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_SESSIONERR)
ctx.JSON(200,errResp)
return
}
然后调用远程服务,把用户名传到远程,并获取返回值,代码如下:
go
//远程调用
grpcService := utils.GetGrpcService()
client := getUserInfo.NewGetUserInfoService("go.micro.srv.getUserInfo",grpcServiceClient())
resp,err := client.Call(context.TODO(),&getUserInfo.Request{Name:userName.(string)})
根据返回值确定返回给前端的数据,代码如下:
go
if err != nil {
fmt.Println(err)
resp.Errno = utils.RECODE_DBERR
resp.Errmsg = utils.RecodeText(utils.RECODE_DBERR)
}
ctx.JSON(200,resp)
3.退出登陆
3.1业务流程图
3.2接口定义
json
#Request:
method: DELETE
url:api/v1.0/session
#data:
no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg":"OK",
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
3.3业务实现
退出登陆实现比较简单,直接删除session即可,首先我们来匹配一下路由,代码如下:
go
r1.DELETE("/session",controller.DeleteSession)
然后在控制器实现删除session返回数据操作,代码如下:
go
func DeleteSession(ctx*gin.Context){
resp := make(map[string]interface{})
//删除session
s := sessions.Default(ctx)
s.Delete("userName")
err := s.Save()
if err != nil {
resp["errno"] = utils.RECODE_SESSIONERR
resp["errmsg"] = utils.RecodeText(utils.RECODE_SESSIONERR)
}else {
resp["errno"] = utils.RECODE_OK
resp["errmsg"] = utils.RecodeText(utils.RECODE_OK)
}
ctx.JSON(200,resp)
}
注意。不管是设置session还是删除session最后都需要调用save方法。
4.登陆业务实现
4.1业务流程图
4.2接口定义
json
#Request:
method: POST
url:api/v1.0/sessions
#data:
{
mobile: "133", //手机号
password: "itcast"//密码
}
#Response
#返回成功:
{
"errno": "0",
"errmsg":"OK",
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
4.3服务端实现
首先是创建服务,代码如下:
shell
$ micro new --type src project/login
然后根据接口定义,修改proto参数,修改后的proto内容如下:
protobuf
syntax = "proto3";
package go.micro.srv.login;
service Login {
rpc Call(Request) returns (Response) {}
}
message Request {
string mobile = 1;
string password = 2;
}
message Response {
string errno = 1;
string errmsg = 2;
string name = 3;
}
然后编译proto文件,生成对应接口,修改Main.go文件,再具体实现登陆业务,登陆业务实现如下:
go
//初始化mysql连接
db,err := model.InitDb()
if err != nil {
rsp.Errno = utils.RECODE_DBERR
rsp.Errmsg = utils.RecodeText(utils.RECODE_DBERR)
}
//对传入的密码进行md5加密,然后调用封装的函数进行登陆校验,注意后面需要把用户名存入session,所以要把name传出
m5 := md5.New()
m5.Write([]byte(req.Password))
pwdHash := hex.EncodeToString(m5.Sum(nil))
result,name := model.GetLoginInfo(db,req.Mobile,pwdHash)
//根据结果返回数据
if result{
rsp.Errno = utils.RECODE_OK
rsp.Errmsg = utils.RecodeText(utils.RECODE_OK)
rsp.Name = name
}else {
rsp.Errno = utils.RECODE_DATAERR
rsp.Errmsg = utils.RecodeText(utils.RECODE_DATAERR)
}
return nil
封装的函数如下:
go
//根据电话号和密码判断,登录是否成功
func GetLoginInfo(db *gorm.DB,mobile string,pwd string)(bool,string){
var user User
db.Where("mobile = ?",mobile).First(&user)
return user.Password_hash == pwd,user.Name
}
4.4web端实现
处理完服务端接着我们来处理web端内容, 首先是做路由匹配,代码如下:
go
r1.POST("/sessions",controller.PostLogin)
然后在具体控制器函数中实现登陆业务,代码如下:
go
//登录处理
type UserLogin struct {
Mobile string `json:"mobile"`
Password string `json:"password"`
}
func PostLogin(ctx*gin.Context){
//定义错误容器
errResp := make(map[string]interface{})
//获取数据
var userLogin UserLogin
err := ctx.Bind(&userLogin)
if err != nil {
errResp["errno"] = utils.RECODE_REQERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_REQERR)
ctx.JSON(200,errResp)
return
}
//远程服务调用
grpcService := utils.GetGrpcService()
client := login.NewLoginService("go.micro.srv.login",grpcService.Client())
resp,err := client.Call(context.TODO(),&login.Request{Mobile:userLogin.Mobile,Password:userLogin.Password})
if err != nil {
fmt.Println(err)
errResp["errno"] = utils.RECODE_LOGINERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_LOGINERR)
ctx.JSON(200,errResp)
return
}
//如果返回成功,存储session
if resp.Errno == utils.RECODE_OK{
s := sessions.Default(ctx)
s.Set("userName",resp.Name)
s.Save()
}
ctx.JSON(200,resp)
}
5.上传用户头像业务
5.1业务流程图
5.2接口定义
go
#Request:
method: POST
url:api/v1.0/user/avatar
#data:
图片的二进制数据
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"avatar_url": "http://101.200.170.171:8888/group1/M00/00/00/Zciqq1n6_L-AOB04AADexS5wJKs662.png" //图片地址需要进行拼接
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
5.3服务端实现
首先创建服务。命令如下:
shell
$ micro new --type srv project/postAva
然后根据接口定义确定proto文件内容,proto文件内容如下:
protobuf
syntax = "proto3";
package go.micro.srv.postAvatar;
service PostAvatar {
rpc Call(Request) returns (Response) {}
}
message Request {
string name = 1;
bytes file = 2;
string fileExt = 3;
}
message Response {
string errno = 1;
string errmsg = 2;
ImgPath data = 3;
}
message ImgPath{
string avatar_url = 1;
}
这里注意,我们把文件存储的时候需要获取文件格式信息,所系需要把文件格式传递过来,头像信息上传之后需要和当前用户绑定,所以需要把用户名传递过来。
然后编译proto并修改main.go文件,再去handler中实现具体业务,业务代码如下:
go
//解析出来文件
client ,_:= fdfs_client.NewFdfsClient("/etc/fdfs/client.conf")
uploadResp ,err := client.UploadByBuffer(req.File,req.FileExt)
if err != nil {
rsp.Errno = utils.RECODE_IOERR
rsp.Errmsg = utils.RecodeText(utils.RECODE_IOERR)
return err
}
//给返回值赋值
rsp.Errno = utils.RECODE_OK
rsp.Errmsg = utils.RecodeText(utils.RECODE_OK)
var imgPath postAvatar.ImgPath
imgPath.AvatarUrl = "http://192.168.137.130:8888/"+uploadResp.RemoteFileId
rsp.Data = &imgPath
return nil
5.4web端实现
具体实现,代码如下:
go
func PostAvatar(ctx*gin.Context){
//定义错误返回容器
errResp := make(map[string]interface{})
//获取前段传递的文件
fileHeader ,err:= ctx.FormFile("avatar")
if err != nil {
errResp["errno"] = utils.RECODE_REQERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_REQERR)
ctx.JSON(200,errResp)
return
}
//判断文件大小及格式是否正确
if fileHeader.Size > 500000{
errResp["errno"] = utils.RECODE_DATAERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_DATAERR)
ctx.JSON(200,errResp)
return
}
fileExt:= path.Ext(fileHeader.Filename)
if fileExt != ".jpg" && fileExt != ".png" && fileExt != ".jpeg"{
errResp["errno"] = utils.RECODE_DATAERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_DATAERR)
ctx.JSON(200,errResp)
return
}
//获取文件流,并写成字节格式
file ,_ :=fileHeader.Open()
buffer := make([]byte,fileHeader.Size)
file.Read(buffer)
//获取用户名
s := sessions.Default(ctx)
userName := s.Get("userName")
//调用远程服务
grpcService := utils.GetGrpcService()
client := postAva.NewPostAvatarService("go.micro.srv.postAvatar",grpcService.Client())
resp ,err:= client.Call(context.TODO(),&postAva.Request{
FileExt:fileExt,
File:buffer,
Name:userName.(string),
})
if err != nil{
errResp["errno"] = utils.RECODE_IOERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_IOERR)
ctx.JSON(200,errResp)
return
}
ctx.JSON(200,resp)
}
6.更新用户名
6.1业务流程图
6.2接口定义
json
#Request:
method: PUT
url:api/v1.0/user/name
#data:
{
"name":"panda"
}
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"name": "Panda"
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
6.3服务端实现
6.4web端实现
7.检查用户实名认证
7.1业务流程图
7.2接口定义
go
#Request:
method: GET
url:api/v1.0/user/auth
#data:
no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"user_id": 1,
"name": "Panda",
"password": "123123",
"mobile": "110",
"real_name": "熊猫",
"id_card": "210112244556677",
"avatar_url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1n7It2ANn1dAADexS5wJKs808.png"
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
7.3服务端实现
7.4web端实现
8.更新用户实名认证
8.1业务流程图
8.2接口定义
json
#Request:
method: POST
url:api/v1.0/user/auth
#data:
{
real_name: "熊猫",
id_card: "21011223344556677"
}
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功"
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
8.3服务端实现
8.4web端实现
9 获取当前用户已发布房源信息
获取用户已发布房源信息服务(商品相关)
创建命令
shell
$ micro new --type "srv" sss/GetUserHouses
流程与接口
json
#Request:
method: GET
url:api/v1.0/user/houses
#data:
no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"houses": [
{
"address": "西三旗桥东",
"area_name": "昌平区",
"ctime": "2017-11-06 11:16:24",
"house_id": 1,
"img_url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBJY-AL3m8AAS8K2x8TDE052.jpg",
"order_count": 0,
"price": 100,
"room_count": 2,
"title": "上奥世纪中心",
"user_avatar": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBLFeALIEjAADexS5wJKs340.png"
},
{
"address": "北清路郑上路",
"area_name": "顺义区",
"ctime": "2017-11-06 11:38:54",
"house_id": 2,
"img_url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBKtmAC8y8AAZcKg5PznU817.jpg",
"order_count": 0,
"price": 1000,
"room_count": 1,
"title": "修正大厦302教室",
"user_avatar": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBLFeALIEjAADexS5wJKs340.png"
}
]
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
服务端实现
根据接口定义,生成对应的proto文件,proto文件内容如下:
protobuf
syntax = "proto3";
package go.micro.srv.getHouse;
service GetHouse {
rpc Call(Request) returns (Response) {}
}
message Request {
string name = 1;
}
message Response {
string errno = 1;
string errmsg = 2;
houses data = 3;
}
message houses{
repeated houseInfo houses = 1;
}
message houseInfo {
string address = 1;
string area_name = 2;
string ctime = 3;
int32 house_id = 4;
string img_url = 5;
int32 order_count = 6;
int32 price = 7;
int32 room_count = 8;
string title = 9;
string user_avatar = 10;
}
然后编译proto文件,接着去main.go文件中,删除不需要的部分,并添加consul服务发现,然后去hander里面处理具体的业务代码,具体代码内容如下:
go
func (e *GetHouse) Call(ctx context.Context, req *getHouse.Request, rsp *getHouse.Response) error {
//查询数据库,根据用户名获取房源信息
db,err :=model.InitDb()
if err != nil{
return err
}
//具体数据库操作,封装成函数,在下面展示
houses,user,areas,err :=model.GetHouses(db,req.Name)
if err != nil {
fmt.Println(err)
return err
}
//根据返回数据,构造返回值
var houseInfos getHouse.Houses
for k,v := range houses{
var house getHouse.HouseInfo
house.Address = v.Address
house.HouseId = int32(v.ID)
house.Ctime = v.CreatedAt.Format("2006-01-02 15:04:05")
house.ImgUrl = v.Index_image_url
house.OrderCount = int32(v.Order_count)
house.Price = int32(v.Price)
house.RoomCount = int32(v.Room_count)
house.Title = v.Title
house.UserAvatar = user.Avatar_url
house.AreaName = areas[k].Name
houseInfos.Houses = append(houseInfos.Houses,&house)
}
fmt.Println("houseInfos = ",houseInfos,"areas = ",areas)
//返回数据
rsp.Errno = utils.RECODE_OK
rsp.Errmsg = utils.RecodeText(utils.RECODE_OK)
rsp.Data = &houseInfos
return nil
}
这里我们把数据库操作封装到函数里面,函数实现如下:
go
//根据用户名获取当前用户的房源信息
func GetHouses(db *gorm.DB, name string) ([]House, User, []Area, error) {
//查询当前用户对象
var user User
err := db.Where("name = ?", name).First(&user).Error
if err != nil {
return nil, User{}, nil, err
}
//查询当前用户对应的房屋
var houses []House
err = db.Model(&user).Related(&houses).Error
if err != nil {
return nil, User{}, nil, err
}
//获取各个房源对应的地址
var areas []Area
for _, v := range houses {
var area Area
err = db.Model(&v).Related(&area).Error
if err != nil {
return nil, User{}, nil, err
}
areas = append(areas, area)
}
return houses, user, areas, nil
}
web端实现
根据设计文档,这里我们给对应请求添加路由,代码如下:
go
r1.GET("/user/houses",controller.GetUserHouses)
然后到controller中实现对应请求方法,代码如下:
go
//获取当前用户发布房源信息
func GetUserHouses(ctx*gin.Context){
//获取用户名
s := sessions.Default(ctx)
userName := s.Get("userName")
//调用远程服务
grpcService := utils.GetGrpcService()
client := getHouse.NewGetHouseService(utils.GetHouseName,grpcService.Client())
resp,err := client.Call(context.TODO(),&getHouse.Request{Name:userName.(string)})
fmt.Println("resp = ",err)
if err != nil {
resp.Errno = utils.RECODE_SERVERERR
resp.Errmsg = utils.RecodeText(utils.RECODE_SERVERERR)
}
ctx.JSON(200,resp)
}
10 发布房源信息
发送(发布)房源信息服务(商品相关)
创建命令
shell
$ micro new --type "srv" sss/PostHouses
流程与接口
json
#Request:
method: POST
url:api/v1.0/houses
#data:
{
"title":"上奥世纪中心",
"price":"666",
"area_id":"5",
"address":"西三旗桥东建材城1号",
"room_count":"2",
"acreage":"60",
"unit":"2室1厅",
"capacity":"3",
"beds":"双人床2张",
"deposit":"200",
"min_days":"3",
"max_days":"0",
"facility":["1","2","3","7","12","14","16","17","18","21","22"]
}
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功"
"data" :{
"house_id": "1"
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
服务端实现
创建服务,然后根据接口定义我们来修改服务的proto文件,proto内容如下:
go
syntax = "proto3";
package go.micro.srv.postHouse;
service PostHouse {
rpc Call(Request) returns (Response) {}
}
message Request {
string acreage = 1;
string address = 2;
string area_id = 3;
string beds = 4;
string capacity = 5;
string deposit = 6;
repeated string facility = 7;
string min_days = 8;
string max_days = 9;
string price = 10;
string room_count = 11;
string title = 12;
string unit = 13;
string name = 14;
}
message Response {
string errno = 1;
string errmsg = 2;
map<string,int32> data = 3;
}
修改main.go文件,然后到handler中实现具体服务内容,代码如下:
go
//发布房源,插入操作
db,err := model.InitDb()
//向数据库中插入数据
var house model.House
acrea,_ :=strconv.Atoi(req.Acreage)
house.Acreage = acrea
house.Address = req.Address
areaId ,_ :=strconv.Atoi(req.AreaId)
house.AreaId = uint(areaId)
house.Beds = req.Beds
cap,_ :=strconv.Atoi(req.Capacity)
house.Capacity = cap
dep ,_ :=strconv.Atoi(req.Deposit)
house.Deposit = dep
maxDays ,_ :=strconv.Atoi(req.MaxDays)
house.Max_days = maxDays
minDays,_ :=strconv.Atoi(req.MinDays)
house.Min_days = minDays
price ,_:= strconv.Atoi(req.Price)
house.Price = price
house.Title = req.Title
house.Unit = req.Unit
roomCount,_ :=strconv.Atoi(req.RoomCount)
house.Room_count = roomCount
//在model中封装插入函数,具体实现请往下看
houseId,err := model.InserHouse(db,req.Name,house,req.Facility)
if err != nil{
return err
}
//设置返回值
rsp.Errno = utils.RECODE_OK
rsp.Errmsg = utils.RecodeText(utils.RECODE_OK)
rsp.Data = map[string]int32{"house_id":int32(houseId)}
return nil
model中插入房源函数实现,代码如下:
go
//发布房源
func InserHouse(db*gorm.DB,name string,house House,fids []string)(uint,error){
//根据用户名获取用户Id
var user User
user.Name = name
err := db.Where("name = ?",name).Find(&user).Error
if err != nil {
return 0, err
}
//关联当前用户
house.UserId = uint(user.ID)
//查询所有家具放到house里面
for _,v := range fids{
id ,_:=strconv.Atoi(v)
var fac Facility
err := db.Where("id = ?",id).Find(&fac).Error
if err != nil{
return 0,err
}
house.Facilities = append(house.Facilities,&fac)
}
return house.ID, db.Create(&house).Error
}
web端实现
根据接口文档定义,给对应请求添加路由对应,代码如下:
go
r1.POST("/houses",controller.PostHouses)
然后到controller中实现具体操作,代码如下:
go
type HouseInfo struct {
Title string `json:"title"`
Acreage string `json:"acreage"`
Address string `json:"address"`
AreaId string `json:"area_id"`
Beds string `json:"beds"`
Capacity string `json:"capacity"`
Deposit string `json:"deposit"`
Facility []string `json:"facility"`
MaxDays string `json:"max_days"`
MinDays string `json:"min_days"`
Price string `json:"price"`
RoomCount string `json:"room_count"`
Unit string `json:"unit"`
}
//发布房源
func PostHouses(ctx*gin.Context){
//定义错误容器
errResp := make(map[string]interface{})
//获取前端传递过来的数据
var houseInfo HouseInfo
err := ctx.Bind(&houseInfo)
if err != nil {
errResp["errno"] = utils.RECODE_REQERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_REQERR)
ctx.JSON(200,errResp)
return
}
fmt.Println("houseInfo = ",houseInfo)
//获取当前用户名
s := sessions.Default(ctx)
userName := s.Get("userName")
//远程服务调用
grpcService := utils.GetGrpcService()
client := postHouse.NewPostHouseService(utils.PostHouseName,grpcService.Client())
resp ,err:=client.Call(context.TODO(),&postHouse.Request{
Name:userName.(string),
Acreage:houseInfo.Acreage,
Address:houseInfo.Address,
AreaId:houseInfo.AreaId,
Beds:houseInfo.Beds,
Capacity:houseInfo.Capacity,
Deposit:houseInfo.Deposit,
Facility:houseInfo.Facility,
MaxDays:houseInfo.MaxDays,
MinDays:houseInfo.MinDays,
Price:houseInfo.Price,
RoomCount:houseInfo.RoomCount,
Title:houseInfo.Title,
Unit:houseInfo.Unit,
})
//返回数据
if err != nil {
fmt.Println(err)
errResp["errno"] = utils.RECODE_SERVERERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_SERVERERR)
ctx.JSON(200,errResp)
return
}
ctx.JSON(200,resp)
}
11 上传房屋图片
发送(上传)房屋图片服务(商品相关)
创建命令
shell
$ micro new --type "srv" sss/PostHousesImage
流程与接口
json
#Request:
method: POST
#3表示房源id
url:api/v1.0/houses/3/images
url:api/v1.0/houses/:id/images
#data:
图片二进制数据
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBLmWAHlsrAAaInSze-cQ719.jpg"
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
服务端实现
创建服务,根据接口定义,修改proto文件,proto文件内容如下:
go
syntax = "proto3";
package go.micro.srv.uploadHouseImage;
service UploadHouseImage {
rpc Call(Request) returns (Response) {}
}
message Request {
int32 house_id = 1;
bytes houseBuffer = 2;
string fileExt = 3;
}
message Response {
string errno = 1;
string errmsg = 2;
map<string,string>data = 3;
}
修改main.go文件,添加consul服务发现,然后实现hander中具体的业务,代码如下:
go
//获取fastDFS操作对象
fdfsClient,err:= fdfs_client.NewFdfsClient("/etc/fdfs/client.conf")
if err != nil{
return err
}
//通过字节切片上传文件到fastDFS
fdfsResp,err := fdfsClient.UploadByBuffer(req.HouseBuffer,req.FileExt)
if err != nil {
return err
}
//把数据存储到数据库,这里把数据库操作封装成函数,具体代码往下看
db,err := model.InitDb()
err = model.UploadHouseImage(db,uint(req.HouseId),utils.NginxPath+fdfsResp.RemoteFileId)
if err != nil {
return err
}
//返回数据
rsp.Errno = utils.RECODE_OK
rsp.Errmsg = utils.RecodeText(utils.RECODE_OK)
rsp.Data = map[string]string{"url":utils.NginxPath+fdfsResp.RemoteFileId}
return nil
models中上传房屋图片的操作代码如下:
go
//上传房屋图片
func UploadHouseImage(db*gorm.DB,houseId uint,imgUrl string)error{
var house House
//查询对应 房屋信息
err := db.First(&house,houseId).Error
if err != nil {
return err
}
if house.Index_image_url == ""{
err = db.Model(&house).Update("index_image_url",imgUrl).Error
if err != nil {
return err
}
}else {
var houseImage HouseImage
houseImage.HouseId = houseId
return db.Create(&houseImage).Error
}
return nil
}
web端实现
根据接口文档,给请求添加对应路由,代码如下:
go
r1.POST("/houses/:id/images",controller.PostHousesImage)
接着到controller中实现具体代码,代码如下:
go
func PostHousesImage(ctx*gin.Context){
errResp := make(map[string]string)
//获取数据
houseId := ctx.Param("id")
//这里我们发现,已经操作上传文件两次了,可以把上传文件操作封装成函数,代码在下面
fileBuffer,fileExt := UploadFile(ctx,errResp,"house_image")
if fileBuffer == nil {
ctx.JSON(200,errResp)
return
}
//调用远程服务
id,_ := strconv.Atoi(houseId)
grpcService := utils.GetGrpcService()
client := uploadHouseImage.NewUploadHouseImageService(utils.UploadHouseImage,grpcService.Client())
resp,err := client.Call(context.TODO(),&uploadHouseImage.Request{
HouseId:int32(id),
FileExt:fileExt,
HouseBuffer:fileBuffer,
})
//返回数据
if err != nil {
fmt.Println(err)
errResp["errno"] = utils.RECODE_SERVERERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_SERVERERR)
ctx.JSON(200,errResp)
return
}
ctx.JSON(200,resp)
}
上传文件的函数封装,代码如下:
go
func UploadFile(ctx*gin.Context,errResp map[string]string,fileName string)([]byte,string){
//获取数据
fileHeader,err := ctx.FormFile(fileName)
//校验数据
if err != nil {
errResp["errno"] = utils.RECODE_DATAERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_DATAERR)
return nil,""
}
//大小校验
if fileHeader.Size > 500000{
errResp["errno"] = utils.RECODE_MOREBIG
errResp["errmsg"] = utils.RecodeText(utils.RECODE_MOREBIG)
return nil,""
}
//格式校验
fileExt := path.Ext(fileHeader.Filename)
if fileExt != ".jpg" && fileExt != ".png" && fileExt != ".jepg"{
errResp["errno"] = utils.RECODE_FORMATERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_FORMATERR)
return nil,""
}
file,_ := fileHeader.Open()
buffer := make([]byte,fileHeader.Size)
file.Read(buffer)
return buffer,fileExt[1:]
}
12 获取房源详细信息
获取房屋详细信息服务(商品相关)
创建命令
shell
$ micro new --type "srv" sss/GetHouseInfo
流程与接口
json
#Request:
method: GET
#1表示房源id
url:api/v1.0/houses/1
url:api/v1.0/houses/:id
#data:
no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"house": {
"acreage": 80,
"address": "西三旗桥东",
"beds": "2双人床",
"capacity": 3,
"comments": [
{
"comment": "评论的内容",
"ctime": "2017-11-12 12:30:30",
"user_name": "评论人的姓名"
},
{
"comment": "评论的内容",
"ctime": "2017-11-12 12:30:30",
"user_name": "评论人的姓名"
},
{
"comment": "评论的内容",
"ctime": "2017-11-12 12:30:30",
"user_name": "评论人的姓名"
}
],
"deposit": 200,
"facilities": [9,11,13,16,19,20,21,23],
"hid": 1,
"img_urls": [
"http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBJY-AL3m8AAS8K2x8TDE052.jpg",
"http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBJZmAYqGWAAaInSze-cQ230.jpg"
],
"max_days": 30,
"min_days": 1,
"price": 100,
"room_count": 2,
"title": "上奥世纪中心",
"unit": "3室3厅",
"user_avatar": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBLFeALIEjAADexS5wJKs340.png",
"user_id": 1,
"user_name": "Panda"
},
"user_id": 1
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
服务端实现
创建服务,然后根据接口定义,修改proto文件,proto文件内容如下:
go
syntax = "proto3";
package go.micro.srv.getHouseDetail;
service GetHouseDetail {
rpc Call(Request) returns (Response) {}
}
message Request {
int32 houseId = 1;
}
message Response {
string errno = 1;
string errmsg = 2;
data data = 3;
}
message data{
houseInfo house = 1;
int32 user_id = 2;
}
message houseInfo {
int32 acreage = 1;
string address = 2;
string beds = 3;
int32 capacity = 4;
repeated commentInfo comments = 5;
int32 deposit = 6;
repeated int32 facilities = 7;
int32 hid = 8;
repeated string img_urls = 9;
int32 max_days = 10;
int32 min_days = 11;
int32 price = 12;
int32 room_count = 13;
string title = 14;
string unit = 15;
string user_avatar = 16;
int32 user_id = 17;
string user_name = 18;
}
message commentInfo{
string comment = 1;
string ctime = 2;
string user_name = 3;
}
修改main.go文件,然后去hander中实现具体业务,代码如下:
go
//从redis数据库中获取数据
var data getHouseDetail.Data
redisConn := model.InitRedis().Get()
houseBuffer, _ := redis.Bytes(redisConn.Do("get", "houseId_"+strconv.Itoa(int(req.HouseId))))
if len(houseBuffer) == 0 {
//如果查不到数据,就从数据库获取
// 获取数据库实例
db, err := model.InitDb()
//把具体的操作封装成函数
house, orders, orderUsers, user, facs, imgs, err := model.GetDetail(db, req.HouseId)
if err != nil {
return err
}
//获取评论数据
var comments []*getHouseDetail.CommentInfo
for k, v := range orders {
var commment getHouseDetail.CommentInfo
commment.Ctime = v.UpdatedAt.Format("2006:01:02 15:04:05")
commment.UserName = orderUsers[k].Name
commment.Comment = v.Comment
comments = append(comments, &commment)
}
//获取家具数据
var fids []int32
for _, v := range facs {
fids = append(fids, int32(v.ID))
}
//获取图片
var imgPaths []string
imgPaths = append(imgPaths, house.Index_image_url)
for _, v := range imgs {
imgPaths = append(imgPaths, v.Url)
}
//设置返回数据
var houseInfo getHouseDetail.HouseInfo
houseInfo.Acreage = int32(house.Acreage)
houseInfo.Address = house.Address
houseInfo.Beds = house.Beds
houseInfo.Capacity = int32(house.Capacity)
houseInfo.Comments = comments
houseInfo.Deposit = int32(house.Deposit)
houseInfo.Facilities = fids
houseInfo.Hid = int32(house.ID)
houseInfo.ImgUrls = imgPaths
houseInfo.MinDays = int32(house.Min_days)
houseInfo.MaxDays = int32(house.Max_days)
houseInfo.Price = int32(house.Price)
houseInfo.RoomCount = int32(house.Room_count)
houseInfo.Title = house.Title
houseInfo.Unit = house.Unit
houseInfo.UserAvatar = user.Avatar_url
houseInfo.UserId = int32(house.UserId)
houseInfo.UserName = user.Name
data.House = &houseInfo
data.UserId = int32(user.ID)
//把查询到的数据存储到redis中
buffer,err := json.Marshal(data)
if err != nil {
return err
}
redisConn.Do("set","houseId_"+strconv.Itoa(int(house.ID)),buffer)
}else {
//如果查到数据,就直接解析数据
err := json.Unmarshal(houseBuffer,data)
if err != nil {
return err
}
}
//返回数据
rsp.Errno = utils.RECODE_OK
rsp.Errmsg = utils.RecodeText(utils.RECODE_OK)
rsp.Data = &data
return nil
model中封装的函数具体内容如下:
go
//根据房屋id获取房屋信息,获取评论,用户信息,家具信息,房屋图片
func GetDetail(db*gorm.DB,houseId int32)(House,[]OrderHouse,[]User,User,[]Facility,[]HouseImage,error){
var house House
var orders []OrderHouse
var orderUsers []User
var user User
var facs []Facility
var imgs []HouseImage
house.ID = uint(houseId)
err := db.Where(&house).First(&house).Error
if err != nil {
return house,orders,orderUsers,user,facs,imgs,err
}
//获取评论信息
err = db.Model(&house).Related(&orders).Error
if err != nil {
return house,orders,orderUsers,user,facs,imgs,err
}
//获取评论用户信息
for _,v := range orders{
var orderUser User
err := db.Model(&v).Related(&orderUser).Error
if err != nil {
return house,orders,orderUsers,user,facs,imgs,err
}
orderUsers = append(orderUsers,orderUser)
}
//获取用户信息
err = db.Model(&house).Related(&user).Error
if err != nil {
return house,orders,orderUsers,user,facs,imgs,err
}
//获取家具信息
err = db.Model(&house).Related(&facs,"Facilities").Error
if err != nil {
return house,orders,orderUsers,user,facs,imgs,err
}
//获取房屋图片
err = db.Model(&house).Related(&imgs).Error
if err != nil {
return house,orders,orderUsers,user,facs,imgs,err
}
return house,orders,orderUsers,user,facs,imgs,nil
}
web端实现
根据接口定义,给请求设置对应路由,代码如下:
go
r1.GET("/houses/:id",controller.GetHouseInfo)
然后去controller中实现对应业务,代码如下:
go
//获取房屋详细信息
func GetHouseInfo(ctx*gin.Context){
//获取数据
errResp := make(map[string]interface{})
houseId := ctx.Param("id")
//校验数据
id,err := strconv.Atoi(houseId)
if err != nil {
errResp["errno"] = utils.RECODE_REQERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_REQERR)
ctx.JSON(200,errResp)
return
}
//调用远程服务
grpcService := utils.GetGrpcService()
client := getDetail.NewGetHouseDetailService(utils.GetDetail,grpcService.Client())
resp,err := client.Call(context.TODO(),&getDetail.Request{HouseId:int32(id)})
//返回数据
if err != nil {
fmt.Println(err)
errResp["errno"] = utils.RECODE_SERVERERR
errResp["errmsg"] = utils.RecodeText(utils.RECODE_SERVERERR)
ctx.JSON(200,errResp)
return
}
ctx.JSON(200,resp)
}
13 获取首页动画图片
获取首页轮播图片服务(首页相关)
创建命令
shell
$ micro new --type "srv" sss/GetIndex
流程与接口
shell
#Request:
method: GET
url:api/v1.0/house/index
#data:
no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"houses": [
{
"house_id": this.Id,
"title": this.Title,
"price": this.Price,
"area_name": this.Area.Name,
"img_url": utils.AddDomain2Url(this.Index_image_url),
"room_count": this.Room_count,
"order_count": this.Order_count,
"address": this.Address,
"user_avatar": utils.AddDomain2Url(this.User.Avatar_url),
"ctime": this.Ctime.Format("2006-01-02 15:04:05"),
},
{
"house_id": this.Id,
"title": this.Title,
"price": this.Price,
"area_name": this.Area.Name,
"img_url": utils.AddDomain2Url(this.Index_image_url),
"room_count": this.Room_count,
"order_count": this.Order_count,
"address": this.Address,
"user_avatar": utils.AddDomain2Url(this.User.Avatar_url),
"ctime": this.Ctime.Format("2006-01-02 15:04:05"),
}
],
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
14 搜索房源
获取(搜索)房源服务(商品相关)
创建命令
shell
$ micro new --type "srv" sss/GetHouses
流程与接口
json
#Request:
method: GET
#adi表示地区编号
#sd表示起始日期
#ed表示结束日期
#sk表示查询方式
#p表示页码
url:api/v1.0/houses?aid=5&sd=2017-11-12&ed=2017-11-30&sk=new
#data:
no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"current_page": 1,
"houses": [
{
"address": "西三旗桥东",
"area_name": "昌平区",
"ctime": "2017-11-06 11:16:24",
"house_id": 1,
"img_url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBJY-AL3m8AAS8K2x8TDE052.jpg",
"order_count": 0,
"price": 100,
"room_count": 2,
"title": "上奥世纪中心13号楼",
"user_avatar": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBLFeALIEjAADexS5wJKs340.png"
},
{
"address": "西三旗桥东",
"area_name": "昌平区",
"ctime": "2017-11-06 11:16:24",
"house_id": 1,
"img_url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBJY-AL3m8AAS8K2x8TDE052.jpg",
"order_count": 0,
"price": 100,
"room_count": 2,
"title": "上奥世纪中心18号楼",
"user_avatar": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBLFeALIEjAADexS5wJKs340.png"
}
],
"total_page": 1
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
15 发布订单
发送(发布)订单服务(订单相关)
创建命令
shell
$ micro new --type "srv" sss/PostOrders
流程与接口
json
#Request:
method: POST
url:api/v1.0/orders
#data:
{
"house_id": "1",
"start_date": "2017-11-11 21:23:49",
"end_date": "2017-11-12 21:23:49",
}
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"order_id":"1"
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
16 请求查看房东/租客订单信息
获取房东/租户订单信息服务(订单相关)
创建命令
shell
$ micro new --type "srv" sss/GetUserOrder
流程与接口
json
#Request:
method: GET
url:api/v1.0/user/orders?role=custom 备注:role=custom为租客查看订单信息
url:api/v1.0/user/orders?role=landlord 备注:role=landlord为房东查看被预定订单信息
#data:
no input data
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功",
"data": {
"orders": [
{
"amount": 200,
"comment": "哈哈拒接",
"ctime": "2017-11-11 21:23:49",
"days": 2,
"end_date": "2017-11-29 16:00:00",
"img_url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBJY-AL3m8AAS8K2x8TDE052.jpg",
"order_id": 3,
"start_date": "2017-11-28 16:00:00",
"status": "REJECTED",//WAIT_ACCPET,WAIT_COMMENT,REJECTED,COMPLETE,CANCELED
"title": "上奥世纪中心"
},
{
"amount": 1500,
"comment": "",
"ctime": "2017-11-11 01:32:10",
"days": 15,
"end_date": "2017-11-24 16:00:00",
"img_url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBJY-AL3m8AAS8K2x8TDE052.jpg",
"order_id": 2,
"start_date": "2017-11-10 16:00:00",
"status": "WAIT_COMMENT",
"title": "上奥世纪中心"
},
{
"amount": 300,
"comment": "",
"ctime": "2017-11-10 01:46:00",
"days": 3,
"end_date": "2017-11-11 16:00:00",
"img_url": "http://101.200.170.171:9998/group1/M00/00/00/Zciqq1oBJY-AL3m8AAS8K2x8TDE052.jpg",
"order_id": 1,
"start_date": "2017-11-09 16:00:00",
"status": "WAIT_COMMENT",
"title": "上奥世纪中心"
}
]
}
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
17 房东同意/拒绝订单
更新房东同意/拒绝订单(订单相关)
创建命令
shell
$ micro new --type "srv" sss/PutOrders
流程与接口
json
#Request:
method: PUT
#4表示订单id
url:api/v1.0/orders/4/status
url:api/v1.0/orders/:id/status
#data:
#"accept"表示接受
#"reject"表示拒绝
{action: "accept"}
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功"
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}
18 用户评价订单信息
更新用户评价订单信息(订单相关)
创建命令
shell
$ micro new --type "srv" sss/PutComment
流程与接口
json
#Request:
method: PUT
#2表示订单id0
url:api/v1.0/orders/2/comment
url:api/v1.0/orders/:id/comment
#data:
{
order_id: "2",
comment: "烂房子!"
}
#Response
#返回成功:
{
"errno": "0",
"errmsg": "成功"
}
#返回失败:
{
"errno": "400x", //状态码
"errmsg":"状态错误信息"
}