<file_sep>package main

import (
	"context"
	"fmt"
	videoProto "fyoukuApi/micro/video/proto"

	"github.com/micro/go-micro"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/etcdv3"
)

func main() {
	reg := etcdv3.NewRegistry(func(op *registry.Options) {
		op.Addrs = []string{"http://127.0.0.1:2379"}
	})
	service := micro.NewService(
		micro.Registry(reg),
	)

	service.Init()

	video := videoProto.NewVideoService("go.micro.srv.fyoukuApi.video", service.Client())

	rsp, err := video.ChannelAdvert(context.TODO(), &videoProto.RequestChannelAdvert{
		ChannelId: "1",
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(rsp)

	rspHot, _ := video.ChannelHotList(context.TODO(), &videoProto.RequestChannelHotList{
		ChannelId: "1",
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(rspHot)
}
<file_sep>package redisClient

import (
	"time"

	"github.com/astaxie/beego"
	"github.com/garyburd/redigo/redis"
)

//直接连接
func Connect() redis.Conn {
	pool, _ := redis.Dial("tcp", beego.AppConfig.String("redisdb"))
	return pool
}

//通过连接池
func PoolConnect() redis.Conn {
	// 建立连接池
	pool := &redis.Pool{
		MaxIdle:     5000,              //最大空闲连接数
		MaxActive:   10000,             //最大连接数
		IdleTimeout: 180 * time.Second, //空闲连接超时时间
		Wait:        true,              //超过最大连接数时，是等待还是报错
		Dial: func() (redis.Conn, error) { //建立链接
			c, err := redis.Dial("tcp", beego.AppConfig.String("redisdb"))
			if err != nil {
				return nil, err
			}
			// 选择db
			//c.Do("SELECT", '')
			return c, nil
		},
	}
	return pool.Get()
}
<file_sep>package controllers

import (
	"context"
	userRpcProto "fyoukuApi/micro/user/proto"
	"fyoukuApi/models"
	"regexp"

	"github.com/astaxie/beego"
)

// Operations about Users
type UserRpcController struct {
	beego.Controller
}

//用户登录
// @router /login/do [*]
func (this *UserRpcController) LoginDo(ctx context.Context, req *userRpcProto.RequestLoginDo, res *userRpcProto.ResponseLoginDo) error {
	var (
		userLoginProto userRpcProto.LoginUser
		isorno         bool
		uid            int
		name           string
	)

	mobile := req.Mobile
	password := req.Password

	if mobile == "" {
		res.Code = 4001
		res.Msg = "手机号不能为空"
		goto ERR
	}
	isorno, _ = regexp.MatchString(`^1(3|4|5|7|8)[0-9]\d{8}$`, mobile)
	if !isorno {
		res.Code = 4002
		res.Msg = "手机号格式不正确"
		goto ERR
	}
	if password == "" {
		res.Code = 4003
		res.Msg = "密码不能为空"
		goto ERR
	}
	uid, name = models.IsMobileLogin(mobile, MD5V(password))
	if uid != 0 {
		userLoginProto.Uid = int64(uid)
		userLoginProto.Username = name
		res.Code = 0
		res.Msg = "登录成功"
		res.Items = &userLoginProto
		res.Count = 1
		return nil
	} else {
		res.Code = 4004
		res.Msg = "手机号或密码不正确"
		goto ERR
	}
ERR:
	res.Items = &userLoginProto
	res.Count = 0
	return nil
}
<file_sep># 微服务 服务发现(go version <= 1.14)
## 1.安装etcd(3.4.7)
   地址:https://github.com/etcd-io/etcd/releases  
   下载后直接运行 目录下的etcd

## 安装go-micro
    1. go get github.com/micro/go-micro
    2. go get github.com/micro/protobuf/{proto,proto-gin-go}
    2.go get github.com/micro/protoc-gen-micro/v2
    3. go get go get github.com/micro/protoc-gin-micro
    4.生成相关protobuf protoc -proto_path=xxx -go_out=xxx -micro_out=xxx xxx.proto

## 安装micro
    1. go get 安装或二进制包安装(2.x)
    2. go get github.com/micro/micro
    3.export MICRO_REGISTRY=etcd
    
 ## 启动micro api
    `micro api --address=0.0.0.0:8085 --handler=api`

## 启动micro web
    `micro web`
    
## 启动所有服务

```shell script
    micro api --address=0.0.0.0:8085 --handler=api
    micro web
    etcd
    micro main
    api main

```
<file_sep>package main

import (
	"context"
	"encoding/json"
	user "fyoukuApi/micro/user/proto"
	"log"
	"strings"
	"time"

	"github.com/micro/go-micro"
	api "github.com/micro/go-micro/api/proto"
	"github.com/micro/go-micro/errors"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/etcdv3"
)

type User struct {
	Client user.UserService
}

func (u *User) LoginDo(ctx context.Context, req *api.Request, rsp *api.Response) error {
	log.Print("收到User.LoginDo API请求")
	//接受参数
	mobile, ok := req.Post["mobile"]
	if !ok || len(mobile.Values) == 0 {
		return errors.BadRequest("go.micro.api.fyoukuApi.user", "mobile为空")
	}
	password, ok := req.Post["password"]
	if !ok || len(password.Values) == 0 {
		return errors.BadRequest("go.micro.api.fyoukuApi.user", "password为空")
	}

	response, err := u.Client.LoginDo(ctx, &user.RequestLoginDo{
		Mobile:   strings.Join(mobile.Values, ""),
		Password: strings.Join(password.Values, ""),
	})
	if err != nil {
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(map[string]interface{}{
		"code":  response.Code,
		"msg":   response.Msg,
		"items": response.Items,
		"count": response.Count,
	})
	rsp.Body = string(b)
	return nil
}

func main() {
	reg := etcdv3.NewRegistry(func(op *registry.Options) {
		op.Addrs = []string{"http://127.0.0.1:2379"}
	})
	service := micro.NewService(
		micro.Registry(reg),
		micro.Name("go.micro.api.fyoukuApi.user"),
		micro.RegisterTTL(time.Second*30),
		micro.RegisterInterval(time.Second*10),
	)

	service.Init()
	service.Server().Handle(
		service.Server().NewHandler(
			&User{Client: user.NewUserService("go.micro.srv.fyoukuApi.user", service.Client())},
		),
	)

	if err := service.Run(); err != nil {
		log.Fatal(err)
	}
}
<file_sep>package controllers

import (
	"context"
	videoRpcProto "fyoukuApi/micro/video/proto"
	"fyoukuApi/models"
	"strconv"

	"github.com/astaxie/beego"
)

// Operations about Users
type VideoRpcController struct {
	beego.Controller
}

func (this *VideoRpcController) ChannelAdvert(ctx context.Context, req *videoRpcProto.RequestChannelAdvert, res *videoRpcProto.ResponseChannelAdvert) error {
	var (
		channelAdvertDatas []*videoRpcProto.ChannelAdvertData
		num                int64
		videos             []models.Advert
		err                error
	)

	channelId, _ := strconv.Atoi(req.ChannelId)

	if channelId == 0 {
		res.Code = 4001
		res.Msg = "必须指定频道"
		goto ERR
	}
	num, videos, err = models.GetChannelAdvert(channelId)

	if err == nil {
		for _, v := range videos {
			var channelAdvertData videoRpcProto.ChannelAdvertData
			channelAdvertData.Id = int64(v.Id)
			channelAdvertData.Title = v.Title
			channelAdvertData.SubTitle = v.SubTitle
			channelAdvertData.Img = v.Img
			channelAdvertData.Url = v.Url
			channelAdvertData.AddTime = v.AddTime
			channelAdvertDatas = append(channelAdvertDatas, &channelAdvertData)
		}
		res.Code = 0
		res.Msg = "success"
		res.Items = channelAdvertDatas
		res.Count = num
		return nil
	} else {
		res.Code = 4004
		res.Msg = "请求数据失败，请稍后重试~"
		goto ERR
	}
ERR:
	res.Items = channelAdvertDatas
	res.Count = 0
	return nil
}

func (this *VideoRpcController) ChannelHotList(ctx context.Context, req *videoRpcProto.RequestChannelHotList, res *videoRpcProto.ResponseChannelHotList) error {
	var (
		channelHotListDatas []*videoRpcProto.ChannelHotListData
		num                 int64
		videos              []models.VideoData
		err                 error
	)

	channelId, _ := strconv.Atoi(req.ChannelId)

	if channelId == 0 {
		res.Code = 4001
		res.Msg = "必须指定频道"
		goto ERR
	}
	num, videos, err = models.GetChannelHotList(channelId)

	if err == nil {
		for _, v := range videos {
			var channelHotListData videoRpcProto.ChannelHotListData
			channelHotListData.Id = int64(v.Id)
			channelHotListData.Title = v.Title
			channelHotListData.SubTitle = v.SubTitle
			channelHotListData.Img = v.Img
			channelHotListData.Img1 = v.Img1
			channelHotListData.IsEnd = int64(v.IsEnd)
			channelHotListData.AddTime = v.AddTime
			channelHotListData.EpisodesCount = int64(v.EpisodesCount)
			channelHotListData.Comment = int64(v.Comment)
			channelHotListDatas = append(channelHotListDatas, &channelHotListData)
		}

		res.Code = 0
		res.Msg = "success"
		res.Items = channelHotListDatas
		res.Count = num
		return nil
	} else {
		res.Code = 4004
		res.Msg = "没有相关内容"
		goto ERR
	}
ERR:
	res.Items = channelHotListDatas
	res.Count = 0
	return nil
}
<file_sep>package models

import (
	"github.com/astaxie/beego/orm"
)

type Advert struct {
	Id       int
	Title    string
	SubTitle string
	AddTime  int64
	Img      string
	Url      string
}

func init() {
	orm.RegisterModel(new(Advert))
}

func GetChannelAdvert(channelId int) (int64, []Advert, error) {
	o := orm.NewOrm()
	var adverts []Advert
	num, err := o.Raw("SELECT id, title, sub_title,img,add_time,url FROM advert WHERE status=1 AND channel_id=? ORDER BY sort DESC LIMIT 1", channelId).QueryRows(&adverts)
	return num, adverts, err
}
<file_sep>package main

import (
	"encoding/json"
	"fmt"
	"fyoukuApi/services/mq"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	beego.LoadAppConfig("ini", "../../conf/app.conf")
	defaultdb := beego.AppConfig.String("defaultdb")
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", defaultdb, 30, 30)

	mq.ConsumerDlx("fyouku.comment.count", "fyouku_comment_count", "fyouku.comment.count.dlx", "fyouku_comment_count_dlx", 10000, callback)
}

func callback(s string) {
	type Data struct {
		VideoId    int
		EpisodesId int
	}
	var data Data
	err := json.Unmarshal([]byte(s), &data)
	if err == nil {
		o := orm.NewOrm()
		//修改视频的总评论数
		o.Raw("UPDATE video SET comment=comment+1 WHERE id=?", data.VideoId).Exec()
		//修改视频剧集的评论数
		o.Raw("UPDATE video_episodes SET comment=comment+1 WHERE id=?", data.EpisodesId).Exec()

		//更新redis排行榜 - 通过MQ来实现
		//创建一个简单模式的MQ
		//把要传递的数据转换为json字符串
		videoObj := map[string]int{
			"VideoId": data.VideoId,
		}
		videoJson, _ := json.Marshal(videoObj)
		mq.Publish("", "fyouku_top", string(videoJson))
	}
	fmt.Printf("msg is :%s\n", s)
}
<file_sep>package main

import (
	"fmt"
	"fyoukuApi/controllers"
	"fyoukuApi/micro/user/proto"
	_ "fyoukuApi/routers"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/etcdv3"
)

func main() {
	beego.LoadAppConfig("ini", "../../conf/app.conf")
	defaultdb := beego.AppConfig.String("defaultdb")
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", defaultdb, 30, 30)

	reg := etcdv3.NewRegistry(func(op *registry.Options) {
		op.Addrs = []string{"http://127.0.0.1:2379"}
	})

	service := micro.NewService(
		micro.Registry(reg),
		micro.Name("go.micro.srv.fyoukuApi.user"),
		micro.RegisterTTL(time.Second*30),
		micro.RegisterInterval(time.Second*10),
	)

	service.Init()
	proto.RegisterUserServiceHandler(service.Server(), new(controllers.UserRpcController))

	if err := service.Run(); err != nil {
		fmt.Println(err)
	}
}
<file_sep>package main

import (
	"context"
	"encoding/json"
	video "fyoukuApi/micro/video/proto"
	"log"
	"strings"
	"time"

	"github.com/micro/go-micro"
	api "github.com/micro/go-micro/api/proto"
	"github.com/micro/go-micro/errors"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/etcdv3"
)

type Video struct {
	Client video.VideoService
}

func (v *Video) ChannelAdvert(ctx context.Context, req *api.Request, rsp *api.Response) error {
	log.Print("收到Video.ChannelAdvert API请求")
	//接受参数
	channelId, ok := req.Get["channelId"]
	if !ok || len(channelId.Values) == 0 {
		return errors.BadRequest("go.micro.api.fyoukuApi.video", "channelId为空")
	}

	response, err := v.Client.ChannelAdvert(ctx, &video.RequestChannelAdvert{
		ChannelId: strings.Join(channelId.Values, ""),
	})
	if err != nil {
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(map[string]interface{}{
		"code":  response.Code,
		"msg":   response.Msg,
		"items": response.Items,
		"count": response.Count,
	})
	rsp.Body = string(b)
	return nil
}
func (v *Video) ChannelHotList(ctx context.Context, req *api.Request, rsp *api.Response) error {
	log.Print("收到Video.ChannelHotList API请求")
	//接受参数
	channelId, ok := req.Get["channelId"]
	if !ok || len(channelId.Values) == 0 {
		return errors.BadRequest("go.micro.api.fyoukuApi.video", "channelId为空")
	}

	response, err := v.Client.ChannelHotList(ctx, &video.RequestChannelHotList{
		ChannelId: strings.Join(channelId.Values, ""),
	})
	if err != nil {
		return err
	}
	rsp.StatusCode = 200
	b, _ := json.Marshal(map[string]interface{}{
		"code":  response.Code,
		"msg":   response.Msg,
		"items": response.Items,
		"count": response.Count,
	})
	rsp.Body = string(b)
	return nil
}

func main() {
	reg := etcdv3.NewRegistry(func(op *registry.Options) {
		op.Addrs = []string{"http://127.0.0.1:2379"}
	})
	service := micro.NewService(
		micro.Registry(reg),
		micro.Name("go.micro.api.fyoukuApi.video"),
		micro.RegisterTTL(time.Second*30),
		micro.RegisterInterval(time.Second*10),
	)

	service.Init()
	service.Server().Handle(
		service.Server().NewHandler(
			&Video{Client: video.NewVideoService("go.micro.srv.fyoukuApi.video", service.Client())},
		),
	)

	if err := service.Run(); err != nil {
		log.Fatal(err)
	}
}
<file_sep>package main

import (
	"context"
	"fmt"
	userProto "fyoukuApi/micro/user/proto"

	"github.com/micro/go-micro"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/etcdv3"
)

func main() {
	reg := etcdv3.NewRegistry(func(op *registry.Options) {
		op.Addrs = []string{"http://127.0.0.1:2379"}
	})
	service := micro.NewService(
		micro.Registry(reg),
	)

	service.Init()

	user := userProto.NewUserService("go.micro.srv.fyoukuApi.user", service.Client())

	rsp, err := user.LoginDo(context.TODO(), &userProto.RequestLoginDo{
		Mobile:   "18600001111",
		Password: "<PASSWORD>",
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(rsp)
}
<file_sep>module fyoukuApi

go 1.15

require (
	github.com/astaxie/beego v1.12.3
	github.com/garyburd/redigo v1.6.2
	github.com/go-sql-driver/mysql v1.5.0
	github.com/golang/protobuf v1.4.2
	github.com/gomodule/redigo v2.0.0+incompatible
	github.com/gorilla/websocket v1.4.2
	github.com/micro/go-micro v1.18.0
	github.com/micro/go-plugins v1.5.1 // indirect
	github.com/smartystreets/goconvey v1.6.4
	github.com/streadway/amqp v1.0.0
)
<file_sep>package controllers

import (
	"fyoukuApi/models"

	"github.com/astaxie/beego"
)

type TopController struct {
	beego.Controller
}

//根据频道获取排行榜
// @router /channel/top [*]
func (this *TopController) ChannelTop() {
	//获取频道ID
	channelId, _ := this.GetInt("channelId")
	if channelId == 0 {
		this.Data["json"] = ReturnError(4001, "必须指定频道")
		this.ServeJSON()
	}
	num, videos, err := models.RedisGetChannelTop(channelId)
	if err == nil {
		this.Data["json"] = ReturnSuccess(0, "success", videos, num)
		this.ServeJSON()
	} else {
		this.Data["json"] = ReturnError(4004, "没有相关内容")
		this.ServeJSON()
	}
}

//根据类型获取排行榜
// @router /type/top [*]
func (this *TopController) TypeTop() {
	typeId, _ := this.GetInt("typeId")
	if typeId == 0 {
		this.Data["json"] = ReturnError(4001, "必须指定类型")
		this.ServeJSON()
	}
	num, videos, err := models.RedisGetTypeTop(typeId)
	if err == nil {
		this.Data["json"] = ReturnSuccess(0, "success", videos, num)
		this.ServeJSON()
	} else {
		this.Data["json"] = ReturnError(4004, "没有相关内容")
		this.ServeJSON()
	}
}
<file_sep>package controllers

import (
	"fmt"
	"fyoukuApi/models"
	"github.com/astaxie/beego"
	"regexp"
	"strconv"
	"strings"
)

// Operations about Users
type UserController struct {
	beego.Controller
}

//用户注册功能
// @router /register/save [post]
func (this *UserController) SaveRegister() {
	var (
		mobile   string
		password string
		err      error
	)
	mobile = this.GetString("mobile")
	password = this.GetString("password")

	if mobile == "" {
		this.Data["json"] = ReturnError(4001, "手机号不能为空")
		this.ServeJSON()
	}
	isorno, _ := regexp.MatchString(`^1(3|4|5|7|8)[0-9]\d{8}$`, mobile)
	if !isorno {
		this.Data["json"] = ReturnError(4002, "手机号格式不正确")
		this.ServeJSON()
	}
	if password == "" {
		this.Data["json"] = ReturnError(4003, "密码不能为空")
		this.ServeJSON()
	}

	//判断手机号是否已经注册
	status := models.IsUserMobile(mobile)
	if status {
		this.Data["json"] = ReturnError(4005, "此手机号已经注册")
		this.ServeJSON()
	} else {
		err = models.UserSave(mobile, MD5V(password))
		if err == nil {
			this.Data["json"] = ReturnSuccess(0, "注册成功", nil, 0)
			this.ServeJSON()
		} else {
			this.Data["json"] = ReturnError(5000, err)
			this.ServeJSON()
		}
	}
}

//用户登录
// @router /login/do [*]
func (this *UserController) LoginDo() {
	mobile := this.GetString("mobile")
	password := this.GetString("password")

	if mobile == "" {
		this.Data["json"] = ReturnError(4001, "手机号不能为空")
		this.ServeJSON()
	}
	isorno, _ := regexp.MatchString(`^1(3|4|5|7|8)[0-9]\d{8}$`, mobile)
	if !isorno {
		this.Data["json"] = ReturnError(4002, "手机号格式不正确")
		this.ServeJSON()
	}
	if password == "" {
		this.Data["json"] = ReturnError(4003, "密码不能为空")
		this.ServeJSON()
	}
	uid, name := models.IsMobileLogin(mobile, MD5V(password))
	if uid != 0 {
		this.Data["json"] = ReturnSuccess(0, "登录成功", map[string]interface{}{"uid": uid, "username": name}, 1)
		this.ServeJSON()
	} else {
		this.Data["json"] = ReturnError(4004, "手机号或密码不正确")
		this.ServeJSON()
	}
}

//批量发送通知消息
// @router /send/message [*]
//func (this *UserController) SendMessageDo() {
//	uids := this.GetString("uids")
//	content := this.GetString("content")
//
//	if uids == "" {
//		this.Data["json"] = ReturnError(4001, "请填写接收人~")
//		this.ServeJSON()
//	}
//	if content == "" {
//		this.Data["json"] = ReturnError(4002, "请填写发送内容")
//		this.ServeJSON()
//	}
//	messageId, err := models.SendMessageDo(content)
//	if err == nil {
//		uidConfig := strings.Split(uids, ",")
//		for _, v := range uidConfig {
//			userId, _ := strconv.Atoi(v)
//			//models.SendMessageUser(userId, messageId)
//			models.SendMessageUserMq(userId, messageId)
//		}
//		this.Data["json"] = ReturnSuccess(0, "发送成功~", "", 1)
//		this.ServeJSON()
//	} else {
//		this.Data["json"] = ReturnError(5000, "发送失败，请联系客服~")
//		this.ServeJSON()
//	}
//}

type SendData struct {
	UserId    int
	MessageId int64
}

//批量发送通知消息
// @router /send/message [*]
func (this *UserController) SendMessageDo() {
	uids := this.GetString("uids")
	content := this.GetString("content")

	if uids == "" {
		this.Data["json"] = ReturnError(4001, "请填写接收人~")
		this.ServeJSON()
	}
	if content == "" {
		this.Data["json"] = ReturnError(4002, "请填写发送内容")
		this.ServeJSON()
	}
	messageId, err := models.SendMessageDo(content)
	if err == nil {
		uidConfig := strings.Split(uids, ",")
		count := len(uidConfig)

		sendChan := make(chan SendData, count)
		closeChan := make(chan bool, count)

		go func() {
			var data SendData
			for _, v := range uidConfig {
				userId, _ := strconv.Atoi(v)
				data.UserId = userId
				data.MessageId = messageId
				sendChan <- data
			}
			close(sendChan)
		}()

		for i := 0; i < 5; i++ {
			go sendMessageFunc(sendChan, closeChan)
		}

		for i := 0; i < 5; i++ {
			<-closeChan
		}
		close(closeChan)

		this.Data["json"] = ReturnSuccess(0, "发送成功~", "", 1)
		this.ServeJSON()
	} else {
		this.Data["json"] = ReturnError(5000, "发送失败，请联系客服~")
		this.ServeJSON()
	}
}
func sendMessageFunc(sendChan chan SendData, closeChan chan bool) {
	for t := range sendChan {
		fmt.Println(t)
		models.SendMessageUserMq(t.UserId, t.MessageId)
	}
	closeChan <- true
}
