每日新闻

每日新闻

GoCN每日新闻资讯
有问必答

有问必答

Go相关的问题,技术相关的问题
文章分享

文章分享

技术文章分享,让知识传播给更多的人
招聘应聘

招聘应聘

为Gopher服务的招聘应聘平台

基于Go语言的论坛系统 bbs-go 3.0.4 发布,欢迎各位大神围观指导~

fuckshit 发表了文章 • 0 个评论 • 182 次浏览 • 1 天前 • 来自相关话题

更新内容优化文章列表加载性能,将加载方式修改为上拉加载更多,这种方式在加载列表时不需要count列表总数量,当数据量大时count很耗时。修改网站样式和配色 ...查看全部

更新内容

  • 优化文章列表加载性能,将加载方式修改为上拉加载更多,这种方式在加载列表时不需要count列表总数量,当数据量大时count很耗时。
  • 修改网站样式和配色,新的样式和配色更加好看。
  • 重构前端页面组件,将公用部分抽象成可复用组件。
  • 重构代码完全遵循eslint配置的规则,让eslint没有警告⚠️。
  • 新增配置项站外链接跳转,开启后站外链接需要用户确认后才能进行跳转。

文档地址

公众号

欢迎关注公众号码农俱乐部获取更多干货资源。

码农俱乐部

交流QQ群

bbs-go-qq.jpg

功能预览

前台页面.png
后台页面.png

课程

bbs-go搭建课程上线啦,快来跟着我一步步搭建属于你的bbs吧。该课程会带领大家一步步的了解并熟悉Go语言开发,如果你是一个Go语言初学者,或者正准备学习Go语言,那么这个课程非常适合你。如果你熟练掌握了本课程中的知识点,相信你就已经入门Go语言开发,并能胜任日常的开发工作了。

Go 是非常年轻的一门语言,它的主要目标是兼具Python等动态语言的开发速度和 C/C++ 等编译型语言的性能与安全性。Go 语言发布以来更是受到中国开发者的青睐,头条、七牛、360、腾讯、阿里等大厂的重要服务都开始使用Go语言构建,国内的Go语言研发岗位的薪水也是很可观的。

gochat - 纯go实现的im即时通讯系统(支持水平扩展)

gochat 发表了文章 • 0 个评论 • 482 次浏览 • 2019-11-04 15:56 • 来自相关话题

项目推荐- 项目地址:https://github.com/LockG ...查看全部

项目推荐
- 项目地址:
https://github.com/LockGit/gochat

- 类别:Go

- 项目描述:

gochat为纯go实现的即时通讯系统,支持私信消息与房间广播消息,各层之间通过rpc通讯,支持水平扩展。
使用redis作为消息存储与投递的载体,相对kafka操作起来更加方便快捷,所以十分轻量。
各层之间基于etcd服务发现,在扩容部署时将会方便很多。
由于go的交叉编译特性,编译后可以快速在各个平台上运行,gochat架构及目录结构清晰,
并且本项目还贴心的提供了docker一键构建所有环境依赖,安装起来十分便捷。

- 推荐理由:
轻量快捷不臃肿,水平可扩展,docker快速构建所有环境,迅速体验im即时通讯,
各层架构清晰,文档说明详细。

系统架构:



服务发现:


消息投递


聊天室预览:

gout 新版本发布,golang实现的http 流式客户端

guonaihong 发表了文章 • 0 个评论 • 345 次浏览 • 2019-10-28 09:09 • 来自相关话题

地址 ...查看全部

地址

    gout github地址

简介

    gout是go写的流式客户端,为提高编码效率而开发。特性丰富,使用方便。

技能树




yiigo 4.x 版本发布

IIInsomnia 发表了文章 • 0 个评论 • 347 次浏览 • 2019-10-25 11:50 • 来自相关话题

# yiigo[![golang](https://img.shields.io/badge/Language-Go-green.svg?style=flat)](https://golang.org)[![GitHub rele ...查看全部

# yiigo

[![golang](https://img.shields.io/badge/Language-Go-green.svg?style=flat)](https://golang.org)
[![GitHub release](https://img.shields.io/github/release/IIInsomnia/yiigo.svg)](https://github.com/iiinsomnia/yiigo/releases/latest)
[![GoDoc](https://godoc.org/github.com/iiinsomnia/yiigo?status.svg)](https://godoc.org/github.com/iiinsomnia/yiigo)
[![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)

The best and the most wanted package for junior gophers, probably.

## Features

- Support [MySQL](https://github.com/go-sql-driver/mysql)
- Support [PostgreSQL](https://github.com/lib/pq)
- Support [MongoDB](https://github.com/mongodb/mongo-go-driver)
- Support [Redis](https://github.com/gomodule/redigo)
- Support [Zipkin](https://github.com/openzipkin/zipkin-go)
- Support [Apollo](https://github.com/philchia/agollo)
- Use [gomail](https://github.com/go-gomail/gomail) for email sending
- Use [toml](https://github.com/pelletier/go-toml) for configuration
- Use [sqlx](https://github.com/jmoiron/sqlx) for SQL executing
- Use [gorm](https://gorm.io/) for ORM operating
- Use [zap](https://github.com/uber-go/zap) for logging

## Requirements

`Go1.11+`

## Installation

```sh
go get github.com/iiinsomnia/yiigo/v4
```

## Usage

#### Config

- `yiigo.toml`

```toml
[app]
env = "dev" # dev | beta | prod
debug = true

[apollo]
app_id = "test"
cluster = "default"
address = "127.0.0.1:8080"
cache_dir = "./"

[apollo.namespace]
# 自定义配置对应的namespace

[db]

[db.default]
driver = "mysql"
dsn = "username:password@tcp(localhost:3306)/dbname?timeout=10s&charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&loc=Local"
# dsn = "host=localhost port=5432 user=root password=secret dbname=test connect_timeout=10 sslmode=disable" # pgsql
max_open_conns = 20
max_idle_conns = 10
conn_max_lifetime = 60 # 秒

[mongo]

[mongo.default]
dsn = "mongodb://username:password@localhost:27017"
connect_timeout = 10 # 秒
pool_size = 10
max_conn_idle_time = 60 # 秒
mode = "primary" # primary | primary_preferred | secondary | secondary_preferred | nearest

[redis]

[redis.default]
address = "127.0.0.1:6379"
password = ""
database = 0
connect_timeout = 10 # 秒
read_timeout = 10 # 秒
write_timeout = 10 # 秒
pool_size = 10
pool_limit = 20
idle_timeout = 60 # 秒
wait_timeout = 10 # 秒
prefill_parallelism = 0

[log]

[log.default]
path = "app.log"
max_size = 500
max_age = 0
max_backups = 0
compress = true

[email]
host = "smtp.exmail.qq.com"
port = 25
username = ""
password = ""
```

- config usage

```go
yiigo.Env("app.env").String()
yiigo.Env("app.debug").Bool()
```

#### Apollo

```go
type QiniuConfig struct {
*yiigo.DefaultApolloConfig
BucketName string `toml:"bucket_name"`
}

var qiniu = &QiniuConfig{DefaultApolloConfig: yiigo.NewDefaultConfig("qiniu", "qiniu")}

if err := yiigo.StartApollo(qiniu); err != nil {
log.Fatal(err)
}
```

#### MySQL

```go
// default db
yiigo.DB().Get(&User{}, "SELECT * FROM `user` WHERE `id` = ?", 1)
yiigo.Orm().First(&User{}, 1)

// other db
yiigo.DB("foo").Get(&User{}, "SELECT * FROM `user` WHERE `id` = ?", 1)
yiigo.Orm("foo").First(&User{}, 1)
```

#### MongoDB

```go
// default mongodb
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

defer cancel()

yiigo.Mongo().Database("test").Collection("numbers").InsertOne(ctx, bson.M{"name": "pi", "value": 3.14159})

// other mongodb
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

defer cancel()

yiigo.Mongo("foo").Database("test").Collection("numbers").InsertOne(ctx, bson.M{"name": "pi", "value": 3.14159})
```

#### Redis

```go
// default redis
conn, err := yiigo.Redis().Get()

if err != nil {
log.Fatal(err)
}

defer yiigo.Redis().Put(conn)

conn.Do("SET", "test_key", "hello world")

// other redis
conn, err := yiigo.Redis("foo").Get()

if err != nil {
log.Fatal(err)
}

defer yiigo.Redis("foo").Put(conn)

conn.Do("SET", "test_key", "hello world")
```

#### Zipkin

```go
reporter := yiigo.NewZipkinHTTPReporter("http://localhost:9411/api/v2/spans")

// sampler
sampler := zipkin.NewModuloSampler(1)
// endpoint
endpoint, _ := zipkin.NewEndpoint("yiigo-zipkin", "localhost")

tracer, err := yiigo.NewZipkinTracer(reporter,
zipkin.WithLocalEndpoint(endpoint),
zipkin.WithSharedSpans(false),
zipkin.WithSampler(sampler),
)

if err != nil {
log.Fatal(err)
}

client, err := tracer.HTTPClient(yiigo.WithZipkinClientOptions(zipkinHttp.ClientTrace(true)))

if err != nil {
log.Fatal(err)
}

b, err := client.Get(context.Background(), "url...",
yiigo.WithRequestHeader("Content-Type", "application/json; charset=utf-8"),
yiigo.WithRequestTimeout(5*time.Second),
)

if err != nil {
log.Fatal(err)
}

fmt.Println(string(b))
```

#### Logger

```go
// default logger
yiigo.Logger().Info("hello world")

// other logger
yiigo.Logger("foo").Info("hello world")
```

## Documentation

- [API Reference](https://godoc.org/github.com/iiinsomnia/yiigo)
- [TOML](https://github.com/toml-lang/toml)
- [Example](https://github.com/iiinsomnia/yiigo-example)

**Enjoy

[开源] gev (支持 websocket 啦): Go 实现基于 Reactor 模式的非阻塞网络库

惜朝 发表了文章 • 0 个评论 • 363 次浏览 • 2019-10-24 11:04 • 来自相关话题

https://github.com/Allenxuxu/gev[gev](https://github.com/Allenxuxu/gev) ...查看全部

https://github.com/Allenxuxu/gev

[gev](https://github.com/Allenxuxu/gev) 是一个轻量、快速、高性能的基于 Reactor 模式的非阻塞网络库,底层并不使用 golang net 库,而是使用 epoll 和 kqueue。

现在它支持 WebSocket 啦!

支持定时任务,延时任务!

⬇️⬇️⬇️

## 特点

- 基于 epoll 和 kqueue 实现的高性能事件循环
- 支持多核多线程
- 动态扩容 Ring Buffer 实现的读写缓冲区
- 异步读写
- SO_REUSEPORT 端口重用支持
- 支持 WebSocket
- 支持定时任务,延时任务

## 性能测试

> 测试环境 Ubuntu18.04
- gev
- gnet
- eviop
- evio
- net (标准库)

### 吞吐量测试




仓库地址: https://github.com/Allenxuxu/gev

是否可以增加一个用 go 编写的开源(国产,或者全部)项目列表呢

DennisMao 回复了问题 • 3 人关注 • 3 个回复 • 280 次浏览 • 2019-10-18 10:52 • 来自相关话题

分享一个 go 的开源项目 - Jenkins 命令行客户端 jcli

回复

linuxsuren 发起了问题 • 1 人关注 • 0 个回复 • 140 次浏览 • 2019-10-16 10:03 • 来自相关话题

Go语言实现字节记录锁

PoormaJin 发表了文章 • 0 个评论 • 191 次浏览 • 2019-10-09 09:48 • 来自相关话题

1、实现文件锁2、实现字节记录锁仓库地址:https://gi ...查看全部

1、实现文件锁

2、实现字节记录锁

仓库地址:https://github.com/jinjiangcc/flock

使用实例:https://github.com/jinjiangcc/flock/blob/master/examples/main.go

BookStack v2.1 发布,功能类似 GitBook 和看云的在线文档管理系统

皇虫 发表了文章 • 2 个评论 • 253 次浏览 • 2019-09-24 12:26 • 来自相关话题

## 程序介绍BookStack,分享知识,共享智慧!知识,因分享,传承久远!BookStack 是基于 Mindoc、使用Go语言的Beego框架开发的功能类似GitBook和看云的在线文档管理系统,实现 ...查看全部


## 程序介绍

BookStack,分享知识,共享智慧!知识,因分享,传承久远!

BookStack 是基于 Mindoc、使用Go语言的Beego框架开发的功能类似GitBook和看云的在线文档管理系统,实现了文档采集、导入、电子书生成以及版本控制等强大的文档功能,并推出了配套的开源微信小程序 [BookChat](https://gitee.com/truthhun/BookChat)。

## 升级日志

- [x] 解决 2.0 版本,初始化管理员失败(密码长度修改导致)的问题
- [x] html转json实现,以兼容各种小程序和uni-app的富文本组件`rich-text`对内容的渲染
- [x] markdown导入功能获取标题优化
- [x] 内容采集持续优化,URL链接替换优化
- [x] 支持隐藏收录入口
- [x] 支持隐藏项目开源入口
- [x] API接口用户注册接口,支持控制每小时和每天的用户注册数量,以避免恶意攻击
- [x] 文档内容,SEO 标题支持获取文档的上一个文档的标题
- [x] `puppeteer`采集优化
- [x] 首页分类,增大块点击范围以优化体验
- [x] 编译的时候版本变量注入
- [x] 管理后台用户列表用户注册时间格式化
- [x] 增加虚拟根目录



## 程序升级

本次升级,数据库表结构有新增和调整,不管还是升级或是新部署时,务必先执行如下命令升级数据库表
```
./BookStack install
```

默认管理员账号密码调整为:
```
admin
admin888
```

详细 [安装部署文档](https://www.bookstack.cn/read/help/Ubuntu.md)

## 相关地址

**BookStack 官网**
- 书栈网:https://www.bookstack.cn

**BookStack 开源地址**
- Gitee(码云)开源: https://gitee.com/truthhun/BookStack
- GitHub 开源: https://github.com/TruthHun/BookStack

**BookStack 配套微信小程序 BookChat 开源地址**
- Gitee(码云)开源:https://gitee.com/truthhun/BookChat
- GitHub 开源:https://github.com/truthhun/BookChat

**BookStack 配套手机APP BookChatApp 开源地址**
- Gitee(码云)开源:https://gitee.com/truthhun/BookChatApp
- GitHub 开源:https://github.com/truthhun/BookChatApp

## 配套微信小程序 BookChat 小程序码

![BookChat](https://images.gitee.com/uploads/images/2019/0812/213324_d6225213_1593004.png "BookChat")

根据Golang定义的接口生成proto文件

Akka 发表了文章 • 0 个评论 • 283 次浏览 • 2019-09-21 08:45 • 来自相关话题

# go2proto **不用了解Protobuf语法也能轻松使用golang开发GRPC服务** go2proto 可以很轻松的根据Golang定义的接口生成proto文件,很大程度简化GRPC服务的开发工 ...查看全部
# go2proto

**不用了解Protobuf语法也能轻松使用golang开发GRPC服务**

go2proto 可以很轻松的根据Golang定义的接口生成proto文件,很大程度简化GRPC服务的开发工作。当公司要使用GRPC开发项目的时候就不用再感叹`学不动了`

## show code

- 创建一个user.go, 写入如下内容

```go
package server

type User interface {
Createuser(request Request) Response
}

type Request struct {
Name string
}
type Response struct {
Result string
}
```

- 生成proto文件

在user.go 同目录下执行 ` go2proto -f user.go` 就会自动在当前目录的proto文件夹生成user.proto 文件

```protobuf
// Code generated by go2proto. DO NOT EDIT.
syntax = "proto3";
package proto;

service User {
rpc Createuser (Request) returns (Response) {}
}

message Request {
string Name = 1;
}

message Response {
string Result = 1;
}
```

是不是很简单呢,可以完全不用了解Protobuf语法,只要用Go定义接口就可以

## 安装

```shell
go get -u github.com/akkagao/go2proto
```

## 使用

安装完执行 go2proto 如果能输出一下内容则说明安装成功

```shell
➜ go2proto git:(master) ✗ go2proto
go2proto version: go2proto/1.0.0
Usage: go2proto [-f] [-t]

Options:
-f string
source file path
-t string
proto file target path (default "proto")
```

-f 参数用于指定 go接口文件

-t 参数用于指定生成的proto文件存储的目录

## 注意事项

由于这里定义服务的go文件只是用于生成proto文件,建议不要在代码中引用这里定义的struct。

切记由于proto中的字段顺序都是有编号的,所以不要轻易删除字段或修改字段顺序。尤其是项目发布后。

重要的事情说三遍:

**不要删除字段,不要修改顺序**

**不要删除字段,不要修改顺序**

**不要删除字段,不要修改顺序**

## 实现方法

使用Go提供的源码解析工具把go文件解析成ast语法树,然后分析ast语法树内容。通过模板生成proto文件。

代码很简单关键代码不到300行,有兴趣可以花几分钟时间看一下。

## 参考资料:

https://www.jianshu.com/p/937d649039ec

https://segmentfault.com/a/1190000020386857

感谢以上两篇博客的作者

gnet: 一个轻量级且高性能的 Go 网络库

panjf2000 发表了文章 • 1 个评论 • 587 次浏览 • 2019-09-18 16:08 • 来自相关话题


gnet












# 博客原文
https://taohuawu.club/go-event-loop-networking-library-gnet

# Github 主页
https://github.com/panjf2000/gnet

欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦。

# 简介

`gnet` 是一个基于 Event-Loop 事件驱动的高性能和轻量级网络库。这个库直接使用 [epoll](https://en.wikipedia.org/wiki/Epoll) 和 [kqueue](https://en.wikipedia.org/wiki/Kqueue) 系统调用而非标准 Golang 网络包:[net](https://golang.org/pkg/net/) 来构建网络应用,它的工作原理类似两个开源的网络库:[libuv](https://github.com/libuv/libuv) 和 [libevent](https://github.com/libevent/libevent)。

这个项目存在的价值是提供一个在网络包处理方面能和 [Redis](http://redis.io)、[Haproxy](http://www.haproxy.org) 这两个项目具有相近性能的Go 语言网络服务器框架。

`gnet` 的亮点在于它是一个高性能、轻量级、非阻塞的纯 Go 实现的传输层(TCP/UDP/Unix-Socket)网络库,开发者可以使用 `gnet` 来实现自己的应用层网络协议,从而构建出自己的应用层网络应用:比如在 `gnet` 上实现 HTTP 协议就可以创建出一个 HTTP 服务器 或者 Web 开发框架,实现 Redis 协议就可以创建出自己的 Redis 服务器等等。

**`gnet` 衍生自另一个项目:`evio`,但是性能更好。**

# 功能

- [高性能](#性能测试) 的基于多线程模型的 Event-Loop 事件驱动
- 内置 Round-Robin 轮询负载均衡算法
- 简洁的 APIs
- 基于 Ring-Buffer 的高效内存利用
- 支持多种网络协议:TCP、UDP、Unix Sockets
- 支持两种事件驱动机制:Linux 里的 epoll 以及 FreeBSD 里的 kqueue
- 支持异步写操作
- 允许多个网络监听地址绑定在一个 Event-Loop 上
- 灵活的事件定时器
- SO_REUSEPORT 端口重用

# 核心设计

## 多线程模型

`gnet` 重新设计开发了一个新内置的多线程模型:『主从 Reactor 多线程』,这也是 `netty` 默认的线程模型,下面是这个模型的原理图:


multi_reactor



它的运行流程如下面的时序图:


reactor



现在我正在 `gnet` 里开发一个新的多线程模型:『带线程/go程池的主从 Reactors 多线程』,并且很快就能完成,这个模型的架构图如下所示:


multi_reactor_thread_pool



它的运行流程如下面的时序图:


multi-reactors



## 通信机制

`gnet` 的『主从 Reactors 多线程』模型是基于 Golang 里的 Goroutines的,一个 Reactor 挂载在一个 Goroutine 上,所以在 `gnet` 的这个网络模型里主 Reactor/Goroutine 与从 Reactors/Goroutines 有海量通信的需求,因此 `gnet` 里必须要有一个能在 Goroutines 之间进行高效率的通信的机制,我没有选择 Golang 里的主流方案:基于 Channel 的 CSP 模型,而是选择了性能更好、基于 Ring-Buffer 的 Disruptor 方案。

所以我最终选择了 [go-disruptor](https://github.com/smartystreets-prototypes/go-disruptor):高性能消息分发队列 LMAX Disruptor 的 Golang 实现。

## 自动扩容的 Ring-Buffer

`gnet` 利用 Ring-Buffer 来缓存 TCP 流数据以及管理内存使用。







# 开始使用

## 安装

```sh
$ go get -u github.com/panjf2000/gnet
```

## 使用示例

```go
// ======================== Echo Server implemented with gnet ===========================

package main

import (
"flag"
"fmt"
"log"
"strings"

"github.com/panjf2000/gnet"
"github.com/panjf2000/gnet/ringbuffer"
)

func main() {
var port int
var loops int
var udp bool
var trace bool
var reuseport bool

flag.IntVar(&port, "port", 5000, "server port")
flag.BoolVar(&udp, "udp", false, "listen on udp")
flag.BoolVar(&reuseport, "reuseport", false, "reuseport (SO_REUSEPORT)")
flag.BoolVar(&trace, "trace", false, "print packets to console")
flag.IntVar(&loops, "loops", 0, "num loops")
flag.Parse()

var events gnet.Events
events.NumLoops = loops
events.OnInitComplete = func(srv gnet.Server) (action gnet.Action) {
log.Printf("echo server started on port %d (loops: %d)", port, srv.NumLoops)
if reuseport {
log.Printf("reuseport")
}
return
}
events.React = func(c gnet.Conn, inBuf *ringbuffer.RingBuffer) (out []byte, action gnet.Action) {
top, tail := inBuf.PreReadAll()
out = append(top, tail...)
inBuf.Reset()

if trace {
log.Printf("%s", strings.TrimSpace(string(top)+string(tail)))
}
return
}
scheme := "tcp"
if udp {
scheme = "udp"
}
log.Fatal(gnet.Serve(events, fmt.Sprintf("%s://:%d", scheme, port)))
}

```

## I/O 事件

`gnet` 目前支持的 I/O 事件如下:

- `OnInitComplete` 当 server 初始化完成之后调用。
- `OnOpened` 当连接被打开的时候调用。
- `OnClosed` 当连接被关闭的时候调用。
- `OnDetached` 当主动摘除连接的时候的调用。
- `React` 当 server 端接收到从 client 端发送来的数据的时候调用。(你的核心业务代码一般是写在这个方法里)
- `Tick` 服务器启动的时候会调用一次,之后就以给定的时间间隔定时调用一次,是一个定时器方法。
- `PreWrite` 预先写数据方法,在 server 端写数据回 client 端之前调用。

# 性能测试

## Linux (epoll)

### 系统参数

```powershell
Go Version: go1.12.9 linux/amd64
OS: Ubuntu 18.04
CPU: 8 Virtual CPUs
Memory: 16.0 GiB
```

### Echo Server

![echolinux.png](https://img.hacpai.com/file/2019/09/echolinux-fca6e6e5.png)


### HTTP Server

![httplinux.png](https://img.hacpai.com/file/2019/09/httplinux-663a0318.png)


## FreeBSD (kqueue)

### 系统参数

```powershell
Go Version: go version go1.12.9 darwin/amd64
OS: macOS Mojave 10.14.6
CPU: 4 CPUs
Memory: 8.0 GiB
```

### Echo Server

![echomac.png](https://img.hacpai.com/file/2019/09/echomac-7a29e0d1.png)


### HTTP Server

![httpmac.png](https://img.hacpai.com/file/2019/09/httpmac-cb6d26ea.png)


# 证书

`gnet` 的源码允许用户在遵循 MIT [开源证书](https://github.com/panjf2000/gnet/blob/master/LICENSE) 规则的前提下使用。

# 待做事项

> gnet 还在持续开发的过程中,所以这个仓库的代码和文档会一直持续更新,如果你对 gnet 感兴趣的话,欢迎给这个开源库贡献你的代码~~

beego 代码自动生成器

猴子 发表了文章 • 0 个评论 • 353 次浏览 • 2019-09-17 18:12 • 来自相关话题

平时我们用 Beego 开发小项目的时候,经常会遇到一个这样的问题,就是同样的业务 ,代码我们要重复写好多次,改的只是一个小小的地方。即使是复制也会显得很繁琐。所以我不自量力搞了个工具帮助我们快速实现增删查改操作。下面来介绍一下这个小工具。 ...查看全部
平时我们用 Beego 开发小项目的时候,经常会遇到一个这样的问题,就是同样的业务 ,代码我们要重复写好多次,改的只是一个小小的地方。即使是复制也会显得很繁琐。所以我不自量力搞了个工具帮助我们快速实现增删查改操作。下面来介绍一下这个小工具。

## Gii beego 自动化代码生成

#### 1.介绍
Gii 是一个为了协助快速开发 beego 项目而创建的项目,通过 Gii 您可以很容易地为你已存在的数据表在你指定的目录创建 Model 以及 Controller 。它基于 beego 为你写好created ,update,put,已经 delete 等操作方法。
> 注意不能完全依靠 Gii 为你生成的东西,你需要检查一下再进行使用。

#### 2.安装

您可以通过如下的方式安装 bee 工具:

```
go get github.com/1920853199/go-gii
```
#### 3.使用
```
package main

import (
"github.com/1920853199/go-gii"
)

func main() {

source := "xxxx:xxxxxxxx@tcp(127.0.0.1)/abc"
gii.Column(source,"article","")

//beego.Run()
}
```

参数介绍
1. 第一个参数 source :数据库连接信息
2. 第二个参数 name 数据表名称
3. 第三个参数 controllerPath 是指定 Controller 生成的路径

> 直接执行这个文件后会在 beego 项目的目录的 models下生成对应数据表的 model,在 controller 下的指定路径生成控制器

结果:

![](http://117.50.7.147:8888/static/uploads/2019091715571981.png)

Controller ```article.go```代码

```
package home

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

type ArticleController struct {
beego.Controller
}

func (c *ArticleController) List() {


limit, _ := beego.AppConfig.Int64("limit") // 一页的数量
page, _ := c.GetInt64("page", 1) // 页数
offset := (page - 1) * limit // 偏移量

o := orm.NewOrm()
obj := new(models.Article)

var data []*models.Article
qs := o.QueryTable(obj)

// 获取数据
_, err := qs.OrderBy("-id").Limit(limit).Offset(offset).All(&data)
if err != nil {
c.Abort("404")
}


/*c.Data["json"]= &data
c.ServeJSON()
c.StopRun()*/


// 统计
count, err := qs.Count()
if err != nil {
c.Abort("404")
}

c.Data["Data"] = &data
c.Data["Count"] = count
c.Data["Limit"] = limit
c.Data["Page"] = page
}

func (c *ArticleController) Put() {
id, err := c.GetInt("id", 0)

if id == 0 {
c.Abort("404")
}

// 基础数据
o := orm.NewOrm()
obj := new(models.Article)
var data []*models.Article
qs := o.QueryTable(obj)
err = qs.Filter("id", id).One(&data)
if err != nil {
c.Abort("404")
}
c.Data["Data"] = data[0]

}

func (c *ArticleController) Update() {

id, _ := c.GetInt("id", 0)


/*c.Data["json"] = c.Input()
c.ServeJSON()
c.StopRun()*/

response := make(map[string]interface{})

o := orm.NewOrm()

obj := models.Article{Id: id}
if o.Read(&obj) == nil {
// 需要补充修改的信息
// 如 :obj.Reply = reply

valid := validation.Validation{}

// 补充需要验证的信息
// 如:valid.Required(message.Reply, "Reply")

if valid.HasErrors() {
// 如果有错误信息,证明验证没通过
// 打印错误信息
for _, err := range valid.Errors {
//log.Println(err.Key, err.Message)
response["msg"] = "新增失败!"
response["code"] = 500
response["err"] = err.Key + " " + err.Message
c.Data["json"] = response
c.ServeJSON()
c.StopRun()
}
}

if _, err := o.Update(&obj); err == nil {
response["msg"] = "修改成功!"
response["code"] = 200
response["id"] = id
} else {
response["msg"] = "修改失败!"
response["code"] = 500
response["err"] = err.Error()
}
} else {
response["msg"] = "修改失败!"
response["code"] = 500
response["err"] = "ID 不能为空!"
}

c.Data["json"] = response
c.ServeJSON()
c.StopRun()
}

func (c *ArticleController) Delete() {
id, _ := c.GetInt("id", 0)

response := make(map[string]interface{})

o := orm.NewOrm()
obj := models.Article{Id: id}

if _, err := o.Delete(&obj); err == nil {
response["msg"] = "删除成功!"
response["code"] = 200
}else{
response["msg"] = "删除失败!"
response["code"] = 500
response["err"] = err.Error()
}

c.Data["json"] = response
c.ServeJSON()
c.StopRun()
}
```

Model ```Article.go```代码
```
package models

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

type Article struct {
Id int
Title string
BusinessType string
BusinessName string
DemandType string
Province string
City string
District string
Address string
Content string `orm:"type(text)"`
PublisherId float64
PublishDate time.Time `orm:"type(datetime)"`
Created time.Time `orm:"auto_now_add;type(datetime)"`
}

func init() {
// 需要在init中注册定义的model
orm.RegisterModel(new(Article))
}
```

> 实现上可能会存在很多的缺点不足,希望各位大佬不吝赐教。最后如果大家有觉得还有一点点存在的价值的话给个星星鼓励一下谢谢各位。Github:https://github.com/1920853199/go-gii

beego 开发的博客 go-blog

猴子 发表了文章 • 3 个评论 • 322 次浏览 • 2019-09-16 12:00 • 来自相关话题

个人博客 项目地址: https://github.com/1920853199/go-blog demo 地址: http://leechan.online ...查看全部
个人博客 
项目地址: https://github.com/1920853199/go-blog
demo 
地址: http://leechan.online






golang的管理后台生成工具

cg33 发表了文章 • 0 个评论 • 444 次浏览 • 2019-09-10 08:40 • 来自相关话题

推荐一个golang的管理后台生成工具 - github地址: [https://github.com/chenhg5/go-admin](https://github.com/chenhg5/go-admin) - De ...查看全部
推荐一个golang的管理后台生成工具

- github地址: [https://github.com/chenhg5/go-admin](https://github.com/chenhg5/go-admin)
- Demo地址: [http://demo.go-admin.cn/admin/login](http://demo.go-admin.cn/admin/login)
- 文档地址:[http://doc.go-admin.cn](http://doc.go-admin.cn)

Go语言实现的Java Stream API

tk103331 发表了文章 • 0 个评论 • 283 次浏览 • 2019-09-04 16:53 • 来自相关话题

学习Go语言时实现的集合操作工具库,类似于Java 8 中新增的Stream API。由于Go语言不支持泛型,所以基于反射实现。只用于学习目的,不要用于生产(PS:当然也不会有人用)。 项目地址:https://github.com/ ...查看全部
学习Go语言时实现的集合操作工具库,类似于Java 8 中新增的Stream API。由于Go语言不支持泛型,所以基于反射实现。只用于学习目的,不要用于生产(PS:当然也不会有人用)。

项目地址:https://github.com/tk103331/stream

集合操作包括生成操作、中间操作和终止操作。
生成操作返回值是Steam对象,相当于数据的源头,可以调用Stream的其他方法;中间操作返回值是Stream对象,可以继续调用Stream的方法,即可以链式调用方法;终止操作不能继续调用方法。


下面介绍下这个库的API:

----------

**数据准备**
后面的操作都是基于集合数据的,先准备一些测试数据。

type student struct {
id int
name string
ageint
scores []int
}

func (s *student) String() string {
return fmt.Sprintf("{id:%d, name:%s, age:%d,scores:%v}", s.id, s.name, s.age, s.scores)
}

func createStudents() []student {
names := []string{"Tom", "Kate", "Lucy", "Jim", "Jack", "King", "Lee", "Mask"}
students := make([]student, 10)
rnd := func(start, end int) int { return rand.Intn(end-start) + start }
for i := 0; i < 10; i++ {
students[i] = student{
id: i + 1,
name: names[rand.Intn(len(names))],
age:rnd(15, 26),
scores: []int{rnd(60, 100), rnd(60, 100), rnd(60, 100)},
}
}
return students
}

type node struct {
id int
next *node
}

func createNodes() *node {
i := 10
n := &node{id: i}
for i > 0 {
i--
n = &node{id: i, next: n}
}
return n
}

### 循环遍历 ForEach ###
循环遍历集合中的每一个元素,需要提供一个包含一个参数的处理函数作为参数,形如 func(o T),循环遍历时会把每个元素作为处理函数的实参。
ForEach 方法是终止操作。

func (s *stream) ForEach(actFunc interface{})

例子:

students := createStudents()
stream, _ := New(students)

stream.ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})

输出:

{id:1, name:Kate, age:16,scores:[67 79 61]}
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:3, name:Lee, age:15,scores:[62 69 68]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:5, name:Mask, age:15,scores:[68 78 67]}
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:8, name:Jack, age:16,scores:[91 65 86]}
{id:9, name:King, age:21,scores:[94 63 93]}
{id:10, name:Jim, age:20,scores:[64 99 93]}

### 迭代器 Iterate ###
It 方法可以从一个迭代器中创建一个Stream对象,迭代器就是一个迭代产生数据的迭代函数,迭代函数形如 func(prev T) (next T,more bool),迭代函数的参数为上一个元素的值,返回值是下一个元素的值,和是否还有更多元素。
It 方法是生成操作。

func It(initValue interface{}, itFunc interface{}) (*stream, error)

Sample:

stream, _ := It(root, func(n *node) (*node, bool) {
return n.next, n.next.next != nil
})
stream.ForEach(func(n *node) {
fmt.Printf("\tnode{id:%d}\n", n.id)
})

Output:

node{id:1}
node{id:2}
node{id:3}
node{id:4}
node{id:5}
node{id:6}
node{id:7}
node{id:8}
node{id:9}
node{id:10}

### 生成器 Generate ###
Gen 方法可以从一个生成器中创建一个Stream对象,生成器就是一个不断产生数据的生成函数,生成函数形如 func() (next T,more bool),生成函数没有参数,返回值是下一个元素的值,和是否还有更多元素。
Gen 方法是生成操作。
Gen 方法和It 方法的区别就是,它可以不依赖上一个元素的值。

func Gen(genFunc interface{}) (*stream, error)

例子:

stream, _ := Gen(func() (int, bool) {
x := rand.Intn(10)
return x, x < 8
})
stream.ForEach(func(x int) {
fmt.Printf("\t%d\n", x)
})

输出:

1
7
7
9

### 过滤 Filter ###
Filter 方法对集合中的元素进行过滤,筛选出符合条件的元素,需要提供一个过滤函数,过滤函数形如func(o T) bool,参数为集合中的元素,返回值是表示该元素是否符合条件。
Filter 方法是中间操作。

func (s *stream) Filter(filterFunc interface{}) *stream

例子:

students := createStudents()
stream, _ := New(students)

stream.Filter(func(s student) bool {
return s.age > 20
}).ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})

输出:

{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:9, name:King, age:21,scores:[94 63 93]}

### 映射 Map ###
Map 方法可以将集合中的每个元素映射为新的值,从而得到一个新的集合,需要提供一个映射函数,形如func(o T1) T2,参数为集合中的元素,返回值是表示该元素映射的新值。
Map 方法是中间操作。

func (s *stream) Map(mapFunc interface{}) *stream

例子:

students := createStudents()
stream, _ := New(students)

stream.Map(func(s student) string {
return s.name
}).ForEach(func(s string) {
fmt.Printf("\t%s\n", s)
})

输出:

Kate
Lee
Lee
Lucy
Mask
Jim
King
Jack
King
Jim

### 打平映射 FlatMap ###
FlatMap 方法可以将集合中每个元素映射为多个元素,返回新的集合包含映射的所有元素。需要提供一个映射函数,形如 func(o T1) []T2,参数为集合中的元素,返回值是表示该元素映射的新值的集合。
FlatMap 方法是中间操作。
FlatMap 方法和Map 方法的区别在于,它可以将集合中每个元素嵌套的集合打平,合并为新的集合。

func (s *stream) FlatMap(mapFunc interface{}) *stream

例子:

students := createStudents()
stream, _ := New(students)
var data []int
stream.FlatMap(func(s student) []int {
return s.scores
}).ToSlice(&data)
fmt.Printf("\t%v\n", data)

输出:

[67 79 61 80 76 80 62 69 68 65 97 86 68 78 67 68 90 75 87 91 89 91 65 86 94 63 93 64 99 93]

### 排序 Sort ###
Sort 方法根据一定队则对集合中的元素进行排序,参数为比较函数,形如func(o1,o2 T) bool,参数为集合中的两个元素,返回值为第一参数是否小于第二个参数。排序算法使用sort中的排序算法。
Sort 方法是中间操作。

func (s *stream) Sort(lessFunc interface{}) *stream

例子:

students := createStudents()
stream, _ := New(students)

stream.Sort(func(s1, s2 student) bool {
return s1.scores[0]+s1.scores[1]+s1.scores[2] > s2.scores[0]+s2.scores[1]+s2.scores[2]
}).ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})

输出:

{id:7, name:King, age:22,scores:[87 91 89]}
{id:10, name:Jim, age:20,scores:[64 99 93]}
{id:9, name:King, age:21,scores:[94 63 93]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:8, name:Jack, age:16,scores:[91 65 86]}
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:5, name:Mask, age:15,scores:[68 78 67]}
{id:1, name:Kate, age:16,scores:[67 79 61]}
{id:3, name:Lee, age:15,scores:[62 69 68]}

### 去重 Distinct ###
Distinct 方法会对集合中的元素进行比较,并将重复的元素过滤掉. 参数为比较函数,形如 func(o1,o2 T) bool,参数为集合中的两个元素,返回值为两个元素是否相等。
Distinct 方法是中间操作。

func (s *stream) Distinct(equalFunc interface{}) *stream

例子:

students := createStudents()
stream, _ := New(students)

stream.Map(func(s student) string {
return s.name
}).Distinct(func(p1, p2 string) bool {
return p1 == p2
}).ForEach(func(s string) {
fmt.Printf("\t%s\n", s)
})

输出:

Kate
Lee
Lucy
Mask
Jim
King
Jack

### 提取 Peek ###
Peek 方法遍历集合的每个元素,执行一定的处理,处理函数形如func(o T),参数为集合每一个元素,没有返回值。
Peek 方法和 ForEach 方法的区别,它是一个中间操作,可以继续调用Stream的其他方法。

func (s *stream) Peek(peekFunc interface{}) *stream

例子:

students := createStudents()
stream, _ := New(students)

stream.Filter(func(s student) bool {
return s.age%2 == 0
}).Call(func() {
fmt.Println("\tfilter by age % 2 == 0")
}).Peek(func(s student) {
fmt.Printf("\t%s\n", s.String())
}).Filter(func(s student) bool {
return s.age > 18
}).Call(func() {
fmt.Println("\tfilter by age > 18")
}).Peek(func(s student) {
fmt.Printf("\t%s\n", s.String())
}).Exec()

输出:

filter by age % 2 == 0
{id:1, name:Kate, age:16,scores:[67 79 61]}
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:8, name:Jack, age:16,scores:[91 65 86]}
{id:10, name:Jim, age:20,scores:[64 99 93]}
filter by age > 18
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:10, name:Jim, age:20,scores:[64 99 93]}

### 调用 Call ###
Call 方法可以在Stream对象执行过程中拿到集合的所有数据,可以对中间结果做一些处理,参数为处理函数,形如func(o []T),参数为整个集合的数据。
Call 方法为中间操作。

func (s *stream) Call(callFunc interface{}) *stream

### 检查 Check ###
Check 方法可以在Stream对象执行过程中检查是否需要进行后续操作,参数为判断函数,形如func(o []T) bool,参数为整个集合的数据,返回值为是否继续处理数据。
Check 方法为中间操作。
Check 方法与Call 方法的区分是,它可以终止整个Steam的执行。

func (s *stream) Check(checkFunc interface{}) *stream

### 限制 Limit ###
Limit 方法可以限制集合中元素的数量,参数为显示的数量。
Limit 方法为中间操作。

func (s *stream) Limit(num int) *stream

例子:

students := createStudents()
stream, _ := New(students)

stream.Limit(5).Call(func() {
fmt.Println("\tlimit by 5")
}).ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})

输出:

limit by 5
{id:1, name:Kate, age:16,scores:[67 79 61]}
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:3, name:Lee, age:15,scores:[62 69 68]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:5, name:Mask, age:15,scores:[68 78 67]}

### 跳过 Skip ###
Skip 方法可以在处理过程中跳过指定数目的元素,参数为跳过的数量.

func (s *stream) Skip(num int) *stream

例子:

stream.Skip(5).Call(func() {
fmt.Println("\tskip by 5")
}).ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})

输出:

skip by 5
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:8, name:Jack, age:16,scores:[91 65 86]}
{id:9, name:King, age:21,scores:[94 63 93]}
{id:10, name:Jim, age:20,scores:[64 99 93]}

### 全部匹配 AllMatch ###
AllMatch 判断集合中的元素是否都符合条件,需要提供一个判断函数,形如 func(o T) bool , 参数为集合中的元素,返回值为是否条件。
AllMatch 方法为终止操作,返回值为是否所有都符合条件。

func (s *stream) AllMatch(matchFunc interface{}) bool

### 任一匹配 AnyMatch ###
AnyMatch 判断集合中的元素是否有任一元素符合条件,需要提供一个判断函数,形如 func(o T) bool ,参数为集合中的元素,返回值为是否条件。
AnyMatch 方法为终止操作,返回值为是否有任一元素符合条件。

func (s *stream) AnyMatch(matchFunc interface{}) bool

### 全不匹配 NoneMatch ###
NoneMatch 判断集合中的元素是否所有元素都不符合条件,需要提供一个判断函数,形如 func(o T) bool ,参数为集合中的元素,返回值为是否条件。
NoneMatch 方法为终止操作,返回值为是否所有元素都不符合条件。

func (s *stream) NoneMatch(matchFunc interface{}) bool

例子:

students := createStudents()
stream, _ := New(students)

r1 := stream.AllMatch(func(s student) bool {
return s.age > 20
})
stream.Reset()
r2 := stream.AnyMatch(func(s student) bool {
return s.name == "Jim"
})
stream.Reset()
r3 := stream.NoneMatch(func(s student) bool {
return s.scores[0]+s.scores[1]+s.scores[2] > 270
})
fmt.Printf("\tAllMatch: %t, AnyMatch: %t, NoneMatch: %t \n", r1, r2, r3)

输出:

AllMatch: false, AnyMatch: true, NoneMatch: true

### 计数 Count ###
Count 返回集合中元素的数量。
Count 为终止操作。

func (s *stream) Count() int

例子:

students := createStudents()
stream, _ := New(students)

r := stream.Count()
fmt.Printf("\t%d\n", r)

输出:

10

### 分组 Group ###
Group 方法可以根据规则,将集合中的元素进行分组,需要提供一个分组函数,形如func(o T1) (key T2,value T3),参数为集合中的元素,返回值为分组的key和value。
Group 方法为终止操作,返回值为分组的map。

func (s *stream) Group(groupFunc interface{}) interface{}\

### 最大值 Max ###
Max 方法返回集合中最大的元素,需要提供一个比较函数,形如func(o1,o2 T) bool,参数为集合中的两个元素,返回值为第一参数是否小于第二个参数。
Max 方法为终止操作。

func (s *stream) Max(lessFunc interface{}) interface{}

### 最小值 Min ###
Min 方法返回集合中最大的元素,需要提供一个比较函数,形如func(o1,o2 T) bool,参数为集合中的两个元素,返回值为第一参数是否小于第二个参数。
Min 方法为终止操作。

func (s *stream) Min(lessFunc interface{}) interface{}

例子:

students := createStudents()
stream, _ := New(students)

r1 := stream.Max(func(s1, s2 student) bool {
return s1.scores[0]+s1.scores[1]+s1.scores[2] < s2.scores[0]+s2.scores[1]+s2.scores[2]
})
stream.Reset()
r2 := stream.Min(func(s1, s2 student) bool {
return s1.scores[0]+s1.scores[1]+s1.scores[2] < s2.scores[0]+s2.scores[1]+s2.scores[2]
})
fmt.Printf("\tMax: %v, Min: %v \n", r1, r2)

输出:

Max: {7 King 22 [87 91 89]}, Min: {3 Lee 15 [62 69 68]}

### 最先匹配 First ###
First 方法返回第一个符合条件的元素,需要提供一个匹配函数,形如 func(o T) bool,参数为集合中的元素,返回值表示该元素是否匹配条件。
First 为终止操作。

func (s *stream) First(matchFunc interface{}) interface{}

### 最后匹配 Last ###
First 方法返回第一个符合条件的元素,需要提供一个匹配函数,形如 func(o T) bool,参数为集合中的元素,返回值表示该元素是否匹配条件。
First 为终止操作。

func (s *stream) Last(matchFunc interface{}) interface{}

### 规约 Reduce ###
Reduce 方法可以基于一个初始值,遍历将规约函数应用于集合中的每个元素,得到最终结果,规约函数形如 func(r T2,o T) T2,参数为前面的元素计算结果和当前元素,返回值为新的结果。
Reduce 为终止操作,返回值为规约计算后的结果。

func (s *stream) Reduce(initValue interface{}, reduceFunc interface{}) interface{}

例子:

students := createStudents()
stream, _ := New(students)

r := 0
r = stream.Map(func(s student) int {
return s.scores[0]
}).Reduce(r, func(sum int, i int) int {
return sum + i
}).(int)
fmt.Printf("\t%d\n", r)

输出:

746