目录
项目启动
拆分原则
1、单一职责(一个服务只做一件事)
2、服务粒度适中
3、考虑团队结构
4、以业务模型切入
5、演进式拆分
6、避免环形依赖和双向依赖
如果还没有安装 beego 的话先安装 beego
$ go get -u -v github.com/astaxie/beego
$ go get -u -v github.com/beego/bee
$ cd $GOPATH/src/github.com/beego/bee
$ go build
$ sudo mv bee /bin/
服务发现的启动
项目开始之前,我们先要启动我们单机版的 consul
$ consul agent -dev
数据库的准备
在 mysql 中创建一个数据库:ihome
$ mysql -uroot -p
# 输入 root 密码
# 创建1个数据库:ihome
mysql>create database if not exists ihome default charset utf8 collate utf8_general_ci;
Web 端
可以先去 Github 中提前查看一下 Web 端的文件结构::https://github.com/lpg-it/ihome/tree/master/ihomeWeb
创建命令
$ cd $GOPATH/src
$ micro new --type "web" ihome/ihomeWeb
Web 服务的调整
修改 main.go
package main
import (
"github.com/julienschmidt/httprouter"
"github.com/micro/go-log"
"net/http"
"github.com/micro/go-web"
)
func main() {
// create new web service
service := web.NewService(
web.Name("go.micro.web.ihomeWeb"),
web.Version("latest"),
web.Address(":8080"),
)
// initialise service
if err := service.Init(); err != nil {
log.Fatal(err)
}
rou := httprouter.New()
rou.NotFound = http.FileServer(http.Dir("html"))
// register html handler
service.Handle("/", rou)
// run service
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
创建工具函数文件夹
# 创建工具函数文件夹
$ mkdir utils
# 进入文件夹创建文件
$ cd utils
# 配置文件读取函数文件
$ sudo vim config.go
# 错误码文件
$ sudo vim error.go
# 字符串拼接文件
$ sudo vim misc.go
配置 文件读取文件 config.go
package utils
import (
"github.com/astaxie/beego"
//使用了beego框架的配置文件读取模块
"github.com/astaxie/beego/config"
)
var (
G_server_name string //项目名称
G_server_addr string //服务器ip地址
G_server_port string //服务器端口
G_redis_addr string //redis ip地址
G_redis_port string //redis port端口
G_redis_dbnum string //redis db 编号
G_mysql_addr string //mysql ip 地址
G_mysql_port string //mysql 端口
G_mysql_dbname string //mysql db name
G_fastdfs_port string //fastdfs 端口
G_fastdfs_addr string //fastdfs ip
)
func InitConfig() {
//从配置文件读取配置信息
//如果项目迁移需要进行修改
appconf, err := config.NewConfig("ini", "/home/lpgit/go/src/ihome/ihomeWeb/conf/app.conf")
if err != nil {
beego.Debug(err)
return
}
G_server_name = appconf.String("appname")
G_server_addr = appconf.String("httpaddr")
G_server_port = appconf.String("httpport")
G_redis_addr = appconf.String("redisaddr")
G_redis_port = appconf.String("redisport")
G_redis_dbnum = appconf.String("redisdbnum")
G_mysql_addr = appconf.String("mysqladdr")
G_mysql_port = appconf.String("mysqlport")
G_mysql_dbname = appconf.String("mysqldbname")
G_fastdfs_port = appconf.String("fastdfsport")
G_fastdfs_addr = appconf.String("fastdfsaddr")
return
}
func init() {
InitConfig()
}
错误码文件 error.go
package utils
const (
RECODE_OK = "0"
RECODE_DBERR = "4001"
RECODE_NODATA = "4002"
RECODE_DATAEXIST = "4003"
RECODE_DATAERR = "4004"
RECODE_SESSIONERR = "4101"
RECODE_LOGINERR = "4102"
RECODE_PARAMERR = "4103"
RECODE_USERERR = "4104"
RECODE_ROLEERR = "4105"
RECODE_PWDERR = "4106"
RECODE_SMSERR = "4017"
RECODE_REQERR = "4201"
RECODE_IPERR = "4202"
RECODE_THIRDERR = "4301"
RECODE_IOERR = "4302"
RECODE_SERVERERR = "4500"
RECODE_UNKNOWERR = "4501"
)
var recodeText = map[string]string{
RECODE_OK: "成功",
RECODE_DBERR: "数据库查询错误",
RECODE_NODATA: "无数据",
RECODE_DATAEXIST: "数据已存在",
RECODE_DATAERR: "数据错误",
RECODE_SESSIONERR: "用户未登录",
RECODE_LOGINERR: "用户登录失败",
RECODE_PARAMERR: "参数错误",
RECODE_USERERR: "用户不存在或未激活",
RECODE_ROLEERR: "用户身份错误",
RECODE_PWDERR: "密码错误",
RECODE_REQERR: "非法请求或请求次数受限",
RECODE_IPERR: "IP受限",
RECODE_THIRDERR: "第三方系统错误",
RECODE_IOERR: "文件读写错误",
RECODE_SERVERERR: "内部错误",
RECODE_UNKNOWERR: "未知错误",
RECODE_SMSERR: "短信失败",
}
func RecodeText(code string) string {
str, ok := recodeText[code]
if ok {
return str
}
return recodeText[RECODE_UNKNOWERR]
}
字符串拼接文件 misc.go
package utils
/* 将url加上 http://IP:PROT/ 前缀 */
//http:// + 127.0.0.1 + :+ 8080 + 请求
func AddDomain2Url(url string) (domain_url string) {
domain_url = "http://" + G_fastdfs_addr + ":" + G_fastdfs_port + "/" + url
return domain_url
}
创建数据库文件
没有下载 mysql 驱动的先下载 mysql
$ go get -u -v github.com/go-sql-driver/mysql
$ mkdir models
# 创建数据库文件
$ sudo vim models.go
models.go
文件内容
package models
import (
"github.com/astaxie/beego"
// 使用了beego的orm模块
"github.com/astaxie/beego/orm"
// go语言的sql的驱动
_ "github.com/go-sql-driver/mysql"
// 已经创建好的工具包
"ihome/ihomeWeb/utils"
"time"
)
/* 用户 table_name = user */
type User struct {
Id int `json:"user_id"` // 用户编号
Name string `orm:"size(32)" json:"name"` // 用户昵称
PasswordHash string `orm:"size(128)" json:"password"` // 用户密码加密的
Mobile string `orm:"size(11);unique" json:"mobile"` // 手机号
RealName string `orm:"size(32)" json:"real_name"` // 真实姓名 实名认证
IdCard string `orm:"size(20)" json:"id_card"` // 身份证号 实名认证
AvatarUrl string `orm:"size(256)" json:"avatar_url"` // 用户头像路径 通过 fastDFS 进行图片存储
Houses []*House `orm:"reverse(many)" json:"houses"` // 用户发布的房屋信息 一个人多套房
Orders []*OrderHouse `orm:"reverse(many)" json:"orders"` // 用户下的订单 一个人多次订单
}
/* 房屋信息 table_name = house */
type House struct {
Id int `json:"house_id"` // 房屋编号
User *User `orm:"rel(fk)" json:"user_id"` // 房屋主人的用户编号 与用户进行关联
Area *Area `orm:"rel(fk)" json:"area_id"` // 归属地的区域编号 和地区表进行关联
Title string `orm:"size(64)" json:"title"` // 房屋标题
Price int `orm:"default(0)" json:"price"` // 单价,单位:分 每次的价格要乘以100
Address string `orm:"size(512)" orm:"default('')" json:"address"` // 地址
RoomCount int `orm:"default(1)" json:"room_count"` // 房间数目
Acreage int `orm:"default(0)" json:"acreage"` // 房屋总面积
Unit string `orm:"size(32)" orm:"default('')" json:"unit"` // 房屋单元,如 几室几厅
Capacity int `orm:"default(1)" json:"capacity"` // 房屋容纳的总人数
Beds string `orm:"size(64)" orm:"default('')" json:"beds"` // 房屋床铺的配置
Deposit int `orm:"default(0)" json:"deposit"` // 押金
MinDays int `orm:"default(1)" json:"min_days"` // 最少入住的天数
MaxDays int `orm:"default(0)" json:"max_days"` // 最多入住的天数 0表示不限制
OrderCount int `orm:"default(0)" json:"order_count"` // 预定完成的该房屋的订单数
IndexImageUrl string `orm:"size(256)" orm:"default('')" json:"index_image_url"` // 房屋主图片路径
Facilities []*Facility `orm:"reverse(many)" json:"facilities"` // 房屋设施 与设施表进行关联
Images []*HouseImage `orm:"reverse(many)" json:"img_urls"` // 房屋的图片 除主要图片之外的其他图片地址
Orders []*OrderHouse `orm:"reverse(many)" json:"orders"` // 房屋的订单 与房屋表进行管理
Ctime time.Time `orm:"auto_now_add;type(datetime)" json:"ctime"`
}
// 首页最高展示的房屋数量
var HomePageMaxHouses int = 5
// 房屋列表页面每页显示条目数
var HouseListPageCapacity int = 2
// 处理房子信息
func (this *House) ToHouseInfo() interface{} {
houseInfo := map[string]interface{}{
"house_id": this.Id,
"title": this.Title,
"price": this.Price,
"area_name": this.Area.Name,
"img_url": utils.AddDomain2Url(this.IndexImageUrl),
"room_count": this.RoomCount,
"order_count": this.OrderCount,
"address": this.Address,
"user_avatar": utils.AddDomain2Url(this.User.AvatarUrl),
"ctime": this.Ctime.Format("2006-01-02 15:04:05"),
}
return houseInfo
}
// 处理 1 个房子的全部信息
func (this *House) ToOneHouseDesc() interface{} {
houseDesc := map[string]interface{}{
"hid": this.Id,
"user_id": this.User.Id,
"user_name": this.User.Name,
"user_avatar": utils.AddDomain2Url(this.User.AvatarUrl),
"title": this.Title,
"price": this.Price,
"address": this.Address,
"room_count": this.RoomCount,
"acreage": this.Acreage,
"unit": this.Unit,
"capacity": this.Capacity,
"beds": this.Beds,
"deposit": this.Deposit,
"min_days": this.MinDays,
"max_days": this.MaxDays,
}
//房屋图片
imgUrls := []string{}
for _, imgUrl := range this.Images {
imgUrls = append(imgUrls, utils.AddDomain2Url(imgUrl.Url))
}
houseDesc["img_urls"] = imgUrls
//房屋设施
facilities := []int{}
for _, facility := range this.Facilities {
facilities = append(facilities, facility.Id)
}
houseDesc["facilities"] = facilities
//评论信息
comments := []interface{}{}
orders := []OrderHouse{}
o := orm.NewOrm()
orderNum, err := o.QueryTable("order_house").Filter("house__id", this.Id).Filter("status", OrderStatusComplete).OrderBy("-ctime").Limit(10).All(&orders)
if err != nil {
beego.Error("select orders comments error, err =", err, "house id = ", this.Id)
}
for i := 0; i < int(orderNum); i++ {
o.LoadRelated(&orders[i], "User")
var username string
if orders[i].User.Name == "" {
username = "匿名用户"
} else {
username = orders[i].User.Name
}
comment := map[string]string{
"comment": orders[i].Comment,
"user_name": username,
"ctime": orders[i].Ctime.Format("2006-01-02 15:04:05"),
}
comments = append(comments, comment)
}
houseDesc["comments"] = comments
return houseDesc
}
/* 区域信息 table_name = area */ //区域信息是需要我们手动添加到数据库中的
type Area struct {
Id int `json:"aid"` // 区域编号 1 2
Name string `orm:"size(32)" json:"aname"` // 区域名字 昌平 海淀
Houses []*House `orm:"reverse(many)" json:"houses"` // 区域所有的房屋 与房屋表进行关联
}
/* 设施信息 table_name = "facility"*/ // 设施信息 需要我们提前手动添加的
type Facility struct {
Id int `json:"fid"` // 设施编号
Name string `orm:"size(32)"` // 设施名字
Houses []*House `orm:"rel(m2m)"` // 都有哪些房屋有此设施 与房屋表进行关联的
}
/* 房屋图片 table_name = "house_image"*/
type HouseImage struct {
Id int `json:"house_image_id"` // 图片 id
Url string `orm:"size(256)" json:"url"` // 图片 url 存放我们房屋的图片
House *House `orm:"rel(fk)" json:"house_id"` // 图片所属房屋编号
}
const (
OrderStatusWaitAccept = "WAIT_ACCEPT" //待接单
OrderStatusWaitPayment = "WAIT_PAYMENT" //待支付
OrderStatusPaid = "PAID" //已支付
OrderStatusWaitComment = "WAIT_COMMENT" //待评价
OrderStatusComplete = "COMPLETE" //已完成
OrderStatusCanceled = "CONCELED" //已取消
OrderStatusRejected = "REJECTED" //已拒单
)
/* 订单 table_name = order */
type OrderHouse struct {
Id int `json:"order_id"` //订单编号
User *User `orm:"rel(fk)" json:"user_id"` //下单的用户编号 //与用户表进行关联
House *House `orm:"rel(fk)" json:"house_id"` //预定的房间编号 //与房屋信息进行关联
BeginDate time.Time `orm:"type(datetime)"` //预定的起始时间
EndDate time.Time `orm:"type(datetime)"` //预定的结束时间
Days int //预定总天数
HousePrice int //房屋的单价
Amount int //订单总金额
Status string `orm:"default(WAIT_ACCEPT)"` //订单状态
Comment string `orm:"size(512)"` //订单评论
Ctime time.Time `orm:"auto_now;type(datetime)" json:"ctime"` //每次更新此表,都会更新这个字段
Credit bool //表示个人征信情况 true表示良好
}
// 处理订单信息
func (this *OrderHouse) ToOrderInfo() interface{} {
orderInfo := map[string]interface{}{
"order_id": this.Id,
"title": this.House.Title,
"img_url": utils.AddDomain2Url(this.House.IndexImageUrl),
"start_date": this.BeginDate.Format("2006-01-02 15:04:05"),
"end_date": this.EndDate.Format("2006-01-02 15:04:05"),
"ctime": this.Ctime.Format("2006-01-02 15:04:05"),
"days": this.Days,
"amount": this.Amount,
"status": this.Status,
"comment": this.Comment,
"credit": this.Credit,
}
return orderInfo
}
// 数据库的初始化
func init() {
//调用什么驱动
orm.RegisterDriver("mysql", orm.DRMySQL)
// set default database
// 连接数据 ( 默认参数 ,mysql数据库 ,"数据库的用户名 :数据库密码@tcp("+数据库地址+":"+数据库端口+")/库名?格式",默认参数)
orm.RegisterDataBase("default", "mysql", "root:lpg123456@tcp("+utils.G_mysql_addr+":"+utils.G_mysql_port+")/ihome?charset=utf8", 30)
// 注册 model 建表
orm.RegisterModel(new(User), new(House), new(Area), new(Facility), new(HouseImage), new(OrderHouse))
// create table
//第一个是别名
// 第二个是是否强制替换模块 如果表变更就将false 换成true 之后再换回来表就便更好来了
//第三个参数是如果没有则同步或创建
orm.RunSyncdb("default", false, true)
}
运行服务并且创建表单
# 创建 conf 文件夹用来存放配置文件
$ mkdir conf
# 创建data.sql文件
$ sudo vim data.sql
datat.sql
文件内容
INSERT INTO `area`(`name`) VALUES ('东城区'),('西城区'),('朝阳区'),('海淀区'),('昌平区'),('丰台区'),('房山区'),('通州区'),('顺义区'),('大兴区'),('怀柔区'),('平谷区'),('密云区'),('延庆区'),('石景山区');
INSERT INTO `facility`(`name`) VALUES('无线网络'),('热水淋浴'),('空调'),('暖气'),('允许吸烟'),('饮水设备'),('牙具'),('香皂'),('拖鞋'),('手纸'),('毛巾'),('沐浴露、洗发露'),('冰箱'),('洗衣机'),('电梯'),('允许做饭'),('允许带宠物'),('允许聚会'),('门禁系统'),('停车位'),('有线网络'),('电视'),('浴缸'),('吃鸡'),('打台球');
修改 main.go
内容
import (
_ "ihome/ihomeWeb/models"
)
登录 mysql
进行数据导入
# 登录mysql
$ mysql -uroot -p
# 输入root密码
Mysql> use ihome;
# 数据的导入
mysql> source ./conf/data.sql;
# 数据检查
mysql> select * from area;
mysql> select * from facility;
创建 app.conf
文件
# 应用名称
appname = ihome
# 地址
httpaddr = 127.0.0.1
# 端口
httpport = 8080
# 数据库地址
mysqladdr = 127.0.0.1
# 数据库端口
mysqlport = 3306
导入前端页面
html 文件下载地址:https://cloud.189.cn/t/IzYbIbQVbEJn (访问码: 2owm)
运行
go run main.go
打开浏览器,输入:127.0.0.1:8080
,就可以看到效果啦。
接下来, 我就带领大家一步一步完成每个服务。
李培冠博客
欢迎访问我的个人网站:
李培冠博客:lpgit.com