abiao лет назад: 4
Родитель
Сommit
04eaa27719

+ 106 - 0
go/gopath/src/fohow.com/apps/helper/aes.go

@@ -0,0 +1,106 @@
+package helper
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+	"errors"
+)
+
+//加密base64
+func EnPwdCode(pwd []byte, key []byte, url bool) (string, error) {
+	result, err := AesEcrypt(pwd, key)
+	if err != nil {
+		return "", err
+	}
+	if url {
+		return base64.URLEncoding.EncodeToString(result), err
+	} else {
+		return base64.StdEncoding.EncodeToString(result), err
+	}
+
+}
+
+//解密
+func DePwdCode(pwd string, key []byte, url bool) ([]byte, error) {
+	var pwdByte []byte
+	var err error
+	if url {
+		pwdByte, err = base64.URLEncoding.DecodeString(pwd)
+		if err != nil {
+			return nil, err
+		}
+		//执行AES解密
+
+	} else {
+		pwdByte, err = base64.StdEncoding.DecodeString(pwd)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return AesDeCrypt(pwdByte, key)
+}
+
+//PKCS7 填充模式
+func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	//Repeat()函数的功能是把切片[]byte{byte(padding)}复制padding个,然后合并成新的字节切片返回
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padtext...)
+}
+
+//填充的反向操作,删除填充字符串
+func PKCS7UnPadding(origData []byte) ([]byte, error) {
+	//获取数据长度
+	length := len(origData)
+	if length == 0 {
+		return nil, errors.New("加密字符串错误!")
+	} else {
+		//获取填充字符串长度
+		unpadding := int(origData[length-1])
+		//截取切片,删除填充字节,并且返回明文
+		return origData[:(length - unpadding)], nil
+	}
+}
+
+//实现加密
+func AesEcrypt(origData []byte, key []byte) ([]byte, error) {
+	//创建加密算法实例
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	//获取块的大小
+	blockSize := block.BlockSize()
+	//对数据进行填充,让数据长度满足需求
+	origData = PKCS7Padding(origData, blockSize)
+	//采用AES加密方法中CBC加密模式
+	blocMode := cipher.NewCBCEncrypter(block, key[:blockSize])
+	crypted := make([]byte, len(origData))
+	//执行加密
+	blocMode.CryptBlocks(crypted, origData)
+	return crypted, nil
+}
+
+//实现解密
+func AesDeCrypt(cypted []byte, key []byte) ([]byte, error) {
+	//创建加密算法实例
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	//获取块大小
+	blockSize := block.BlockSize()
+	//创建加密客户端实例
+	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
+	origData := make([]byte, len(cypted))
+	//这个函数也可以用来解密
+	blockMode.CryptBlocks(origData, cypted)
+	//去除填充字符串
+	origData, err = PKCS7UnPadding(origData)
+	if err != nil {
+		return nil, err
+	}
+	return origData, err
+}

+ 174 - 0
go/gopath/src/fohow.com/apps/helper/array.go

@@ -0,0 +1,174 @@
+package helper
+
+type Array struct {
+	data []interface{} // 泛型数组
+	size int           // 元素数量
+}
+
+type ArrayInterface interface {
+	// 添加
+	Add(int, interface{}) // 插入元素
+	AddLast(interface{})
+	AddFirst(interface{})
+	// 删除
+	Remove(int) interface{}
+	RemoveFirst() interface{}
+	RemoveLast() interface{}
+	// 查找
+	Find(interface{}) int      // 查找元素返回第一个索引
+	FindAll(interface{}) []int // 查找元素返回所有索引
+	Contains(interface{}) bool // 查找是否存在元素
+	Get(int) interface{}
+	// 修改
+	Set(int, interface{})
+	// 基本方法
+	GetCapacity() int // 获得数组容量
+	GetSize() int     // 获得元素个数
+	IsEmpty() bool    // 查看数组是否为空
+}
+
+// 获得自定义数组,参数为数组的初始长度
+func GetArray(capacity int) *Array {
+	arr := &Array{}
+	arr.data = make([]interface{}, capacity)
+	arr.size = 0
+	return arr
+}
+
+// 获得数组容量
+func (a *Array) GetCapacity() int {
+	return len(a.data)
+}
+
+// 获得数组元素个数
+func (a *Array) GetSize() int {
+	return a.size
+}
+
+// 判断数组是否为空
+func (a *Array) IsEmpty() bool {
+	return a.size == 0
+}
+
+// newCapacity 新数组容量
+// 逻辑:声明新的数组,将原数组的值 copy 到新数组中
+func (a *Array) resize(newCapacity int) {
+	newArr := make([]interface{}, newCapacity)
+	for i := 0; i < a.size; i++ {
+		newArr[i] = a.data[i]
+	}
+	a.data = newArr
+}
+
+// 获得元素的首个索引,不存在则返回 -1
+func (a *Array) Find(element interface{}) int {
+	for i := 0; i < a.size; i++ {
+		if element == a.data[i] {
+			return i
+		}
+	}
+	return -1
+}
+
+// 获得元素的所有索引,返回索引组成的切片
+func (a *Array) FindAll(element interface{}) (indexes []int) {
+	for i := 0; i < a.size; i++ {
+		if element == a.data[i] {
+			indexes = append(indexes, i)
+		}
+	}
+	return
+}
+
+// 查看数组是否存在元素,返回 bool
+func (a *Array) Contains(element interface{}) bool {
+	if a.Find(element) == -1 {
+		return false
+	}
+	return true
+}
+
+// 获得索引对应元素,需要判断索引有效范围
+func (a *Array) Get(index int) interface{} {
+	if index < 0 || index > a.size-1 {
+		panic("Get failed, index is illegal.")
+	}
+	return a.data[index]
+}
+
+//修改索引对应元素值
+func (a *Array) Set(index int, element interface{}) {
+	if index < 0 || index > a.size-1 {
+		panic("Set failed, index is illegal.")
+	}
+	a.data[index] = element
+}
+func (a *Array) Add(index int, element interface{}) {
+	if index < 0 || index > a.GetCapacity() {
+		panic("Add failed, require index >= 0 and index <= capacity")
+	}
+	// 数组已满则扩容
+	if a.size == len(a.data) {
+		a.resize(2 * a.size)
+	}
+	// 将插入的索引位置之后的元素后移,腾出插入位置
+	for i := a.size - 1; i >= index; i-- {
+		a.data[i+1] = a.data[i]
+	}
+	a.data[index] = element
+	// 维护数组元素的数量
+	a.size++
+}
+
+func (a *Array) AddLast(element interface{}) {
+	a.Add(a.size, element)
+}
+
+func (a *Array) AddFirst(element interface{}) {
+	a.Add(0, element)
+}
+
+func (a *Array) Remove(index int) interface{} {
+	if index < 0 || index >= a.size {
+		panic("Remove failed, index is illegal.")
+	}
+
+	removeEle := a.data[index]
+	// 从 index 之后的元素,都向前移动一个位置
+	for i := index + 1; i < a.size; i++ {
+		a.data[i-1] = a.data[i]
+	}
+	a.size--
+	// 清理最后一个元素
+	a.data[a.size] = nil
+
+	// 考虑边界情况,不能 resize 为0
+	if a.size == len(a.data)/4 && len(a.data)/2 != 0 {
+		a.resize(len(a.data) / 2)
+	}
+	return removeEle
+}
+
+func (a *Array) RemoveFirst() interface{} {
+	return a.Remove(0)
+}
+
+func (a *Array) RemoveLast() interface{} {
+	return a.Remove(a.size - 1)
+}
+
+//重写 String 方法
+//重写数组打印时的展示形式,只需要重写 String 方法
+//func (a *Array) String() string {
+//	var buffer bytes.Buffer
+//	buffer.WriteString(fmt.Sprintf("Array: size = %d, capacity = %d\n", a.size, a.GetCapacity()))
+//	buffer.WriteString("[")
+//	for i := 0; i < a.size; i++ {
+//		buffer.WriteString(fmt.Sprint(a.data[i]))
+//		if i != a.size - 1 {
+//			buffer.WriteString(",")
+//		}
+//	}
+//	buffer.WriteString("]")
+//	return buffer.String()
+//}//

+ 137 - 0
go/gopath/src/fohow.com/apps/helper/captcha/bilinear.go

@@ -0,0 +1,137 @@
+package captcha
+
+// Bilinear Interpolation 双线性插值
+// 引用自 code.google.com/p/graphics-go/interp
+// 主要处理旋转验证码后消除锯齿
+
+import (
+	"image"
+	"image/color"
+	"math"
+)
+
+var bili = Bilinear{}
+
+type Bilinear struct{}
+
+func (Bilinear) RGBA(src *image.RGBA, x, y float64) color.RGBA {
+	p := findLinearSrc(src.Bounds(), x, y)
+
+	// Array offsets for the surrounding pixels.
+	off00 := offRGBA(src, p.low.X, p.low.Y)
+	off01 := offRGBA(src, p.high.X, p.low.Y)
+	off10 := offRGBA(src, p.low.X, p.high.Y)
+	off11 := offRGBA(src, p.high.X, p.high.Y)
+
+	var fr, fg, fb, fa float64
+
+	fr += float64(src.Pix[off00+0]) * p.frac00
+	fg += float64(src.Pix[off00+1]) * p.frac00
+	fb += float64(src.Pix[off00+2]) * p.frac00
+	fa += float64(src.Pix[off00+3]) * p.frac00
+
+	fr += float64(src.Pix[off01+0]) * p.frac01
+	fg += float64(src.Pix[off01+1]) * p.frac01
+	fb += float64(src.Pix[off01+2]) * p.frac01
+	fa += float64(src.Pix[off01+3]) * p.frac01
+
+	fr += float64(src.Pix[off10+0]) * p.frac10
+	fg += float64(src.Pix[off10+1]) * p.frac10
+	fb += float64(src.Pix[off10+2]) * p.frac10
+	fa += float64(src.Pix[off10+3]) * p.frac10
+
+	fr += float64(src.Pix[off11+0]) * p.frac11
+	fg += float64(src.Pix[off11+1]) * p.frac11
+	fb += float64(src.Pix[off11+2]) * p.frac11
+	fa += float64(src.Pix[off11+3]) * p.frac11
+
+	var c color.RGBA
+	c.R = uint8(fr + 0.5)
+	c.G = uint8(fg + 0.5)
+	c.B = uint8(fb + 0.5)
+	c.A = uint8(fa + 0.5)
+	return c
+}
+
+type BilinearSrc struct {
+	// Top-left and bottom-right interpolation sources
+	low, high image.Point
+	// Fraction of each pixel to take. The 0 suffix indicates
+	// top/left, and the 1 suffix indicates bottom/right.
+	frac00, frac01, frac10, frac11 float64
+}
+
+func findLinearSrc(b image.Rectangle, sx, sy float64) BilinearSrc {
+	maxX := float64(b.Max.X)
+	maxY := float64(b.Max.Y)
+	minX := float64(b.Min.X)
+	minY := float64(b.Min.Y)
+	lowX := math.Floor(sx - 0.5)
+	lowY := math.Floor(sy - 0.5)
+	if lowX < minX {
+		lowX = minX
+	}
+	if lowY < minY {
+		lowY = minY
+	}
+
+	highX := math.Ceil(sx - 0.5)
+	highY := math.Ceil(sy - 0.5)
+	if highX >= maxX {
+		highX = maxX - 1
+	}
+	if highY >= maxY {
+		highY = maxY - 1
+	}
+
+	// In the variables below, the 0 suffix indicates top/left, and the
+	// 1 suffix indicates bottom/right.
+
+	// Center of each surrounding pixel.
+	x00 := lowX + 0.5
+	y00 := lowY + 0.5
+	x01 := highX + 0.5
+	y01 := lowY + 0.5
+	x10 := lowX + 0.5
+	y10 := highY + 0.5
+	x11 := highX + 0.5
+	y11 := highY + 0.5
+
+	p := BilinearSrc{
+		low:  image.Pt(int(lowX), int(lowY)),
+		high: image.Pt(int(highX), int(highY)),
+	}
+
+	// Literally, edge cases. If we are close enough to the edge of
+	// the image, curtail the interpolation sources.
+	if lowX == highX && lowY == highY {
+		p.frac00 = 1.0
+	} else if sy-minY <= 0.5 && sx-minX <= 0.5 {
+		p.frac00 = 1.0
+	} else if maxY-sy <= 0.5 && maxX-sx <= 0.5 {
+		p.frac11 = 1.0
+	} else if sy-minY <= 0.5 || lowY == highY {
+		p.frac00 = x01 - sx
+		p.frac01 = sx - x00
+	} else if sx-minX <= 0.5 || lowX == highX {
+		p.frac00 = y10 - sy
+		p.frac10 = sy - y00
+	} else if maxY-sy <= 0.5 {
+		p.frac10 = x11 - sx
+		p.frac11 = sx - x10
+	} else if maxX-sx <= 0.5 {
+		p.frac01 = y11 - sy
+		p.frac11 = sy - y01
+	} else {
+		p.frac00 = (x01 - sx) * (y10 - sy)
+		p.frac01 = (sx - x00) * (y11 - sy)
+		p.frac10 = (x11 - sx) * (sy - y00)
+		p.frac11 = (sx - x10) * (sy - y01)
+	}
+
+	return p
+}
+
+func offRGBA(src *image.RGBA, x, y int) int {
+	return (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4
+}

+ 273 - 0
go/gopath/src/fohow.com/apps/helper/captcha/captcha.go

@@ -0,0 +1,273 @@
+package captcha
+
+import (
+	"github.com/golang/freetype"
+	"github.com/golang/freetype/truetype"
+	"image"
+	"image/color"
+	"image/draw"
+	"io/ioutil"
+	"math"
+	"math/rand"
+	"time"
+)
+
+type Captcha struct {
+	frontColors []color.Color
+	bkgColors   []color.Color
+	disturlvl   DisturLevel
+	fonts       []*truetype.Font
+	size        image.Point
+}
+
+type StrType int
+
+const (
+	NUM   StrType = iota // 数字
+	LOWER                // 小写字母
+	UPPER                // 大写字母
+	ALL                  // 全部
+	CLEAR                // 去除部分易混淆的字符
+)
+
+type DisturLevel int
+
+const (
+	NORMAL DisturLevel = 4
+	MEDIUM DisturLevel = 8
+	HIGH   DisturLevel = 16
+)
+
+func New() *Captcha {
+	c := &Captcha{
+		disturlvl: HIGH,
+		size:      image.Point{82, 32},
+	}
+	c.frontColors = []color.Color{color.Black}
+	c.bkgColors = []color.Color{color.Transparent}
+	c.SetFont("config/font/comic.ttf")
+	return c
+}
+
+// AddFont 添加一个字体
+func (c *Captcha) AddFont(path string) error {
+	fontdata, erro := ioutil.ReadFile(path)
+	if erro != nil {
+		return erro
+	}
+	font, erro := freetype.ParseFont(fontdata)
+	if erro != nil {
+		return erro
+	}
+	if c.fonts == nil {
+		c.fonts = []*truetype.Font{}
+	}
+	c.fonts = append(c.fonts, font)
+	return nil
+}
+
+//AddFontFromBytes allows to load font from slice of bytes, for example, load the font packed by https://github.com/jteeuwen/go-bindata
+func (c *Captcha) AddFontFromBytes(contents []byte) error {
+	font, err := freetype.ParseFont(contents)
+	if err != nil {
+		return err
+	}
+	if c.fonts == nil {
+		c.fonts = []*truetype.Font{}
+	}
+	c.fonts = append(c.fonts, font)
+	return nil
+}
+
+// SetFont 设置字体 可以设置多个
+func (c *Captcha) SetFont(paths ...string) error {
+	for _, v := range paths {
+		if erro := c.AddFont(v); erro != nil {
+			return erro
+		}
+	}
+	return nil
+}
+
+func (c *Captcha) SetDisturbance(d DisturLevel) {
+	if d > 0 {
+		c.disturlvl = d
+	}
+}
+
+func (c *Captcha) SetFrontColor(colors ...color.Color) {
+	if len(colors) > 0 {
+		c.frontColors = c.frontColors[:0]
+		for _, v := range colors {
+			c.frontColors = append(c.frontColors, v)
+		}
+	}
+}
+
+func (c *Captcha) SetBkgColor(colors ...color.Color) {
+	if len(colors) > 0 {
+		c.bkgColors = c.bkgColors[:0]
+		for _, v := range colors {
+			c.bkgColors = append(c.bkgColors, v)
+		}
+	}
+}
+
+func (c *Captcha) SetSize(w, h int) {
+	if w < 48 {
+		w = 48
+	}
+	if h < 20 {
+		h = 20
+	}
+	c.size = image.Point{w, h}
+}
+
+func (c *Captcha) randFont() *truetype.Font {
+	return c.fonts[rand.Intn(len(c.fonts))]
+}
+
+// 绘制背景
+func (c *Captcha) drawBkg(img *Image) {
+	/*ra := rand.New(rand.NewSource(time.Now().UnixNano()))
+	//填充主背景色
+	bgcolorindex := ra.Intn(len(c.bkgColors))
+	bkg := image.NewUniform(c.bkgColors[bgcolorindex])
+	img.FillBkg(bkg)*/
+}
+
+// 绘制噪点
+func (c *Captcha) drawNoises(img *Image) {
+	ra := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	// 待绘制图片的尺寸
+	size := img.Bounds().Size()
+	dlen := int(c.disturlvl)
+	// 绘制干扰斑点
+	for i := 0; i < dlen; i++ {
+		x := ra.Intn(size.X)
+		y := ra.Intn(size.Y)
+		r := ra.Intn(size.Y/20) + 1
+		colorindex := ra.Intn(len(c.frontColors))
+		img.DrawCircle(x, y, r, i%4 != 0, c.frontColors[colorindex])
+	}
+
+	// 绘制干扰线
+	for i := 0; i < dlen; i++ {
+		x := ra.Intn(size.X)
+		y := ra.Intn(size.Y)
+		o := int(math.Pow(-1, float64(i)))
+		w := ra.Intn(size.Y) * o
+		h := ra.Intn(size.Y/10) * o
+		colorindex := ra.Intn(len(c.frontColors))
+		img.DrawLine(x, y, x+w, y+h, c.frontColors[colorindex])
+		colorindex++
+	}
+
+}
+
+// 绘制文字
+func (c *Captcha) drawString(img *Image, str string) {
+
+	if c.fonts == nil {
+		panic("没有设置任何字体")
+	}
+	tmp := NewImage(c.size.X, c.size.Y)
+
+	// 文字大小为图片高度的 0.6
+	fsize := int(float64(c.size.Y) * 0.6)
+	// 用于生成随机角度
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	// 文字之间的距离
+	// 左右各留文字的1/4大小为内部边距
+	padding := fsize / 4
+	gap := (c.size.X - padding*2) / (len(str))
+
+	// 逐个绘制文字到图片上
+	for i, char := range str {
+		// 创建单个文字图片
+		// 以文字为尺寸创建正方形的图形
+		str := NewImage(fsize, fsize)
+		// str.FillBkg(image.NewUniform(color.Black))
+		// 随机取一个前景色
+		colorindex := r.Intn(len(c.frontColors))
+
+		//随机取一个字体
+		font := c.randFont()
+		str.DrawString(font, c.frontColors[colorindex], string(char), float64(fsize))
+
+		// 转换角度后的文字图形
+		rs := str.Rotate(float64(r.Intn(40) - 20))
+		// 计算文字位置
+		s := rs.Bounds().Size()
+		left := i*gap + padding
+		top := (c.size.Y - s.Y) / 2
+		// 绘制到图片上
+		draw.Draw(tmp, image.Rect(left, top, left+s.X, top+s.Y), rs, image.ZP, draw.Over)
+	}
+
+	if c.size.Y >= 48 {
+		// 高度大于48添加波纹 小于48波纹影响用户识别
+		tmp.distortTo(float64(fsize)/10, 200.0)
+	}
+
+	/*font := c.randFont()
+	t := strings.ToUpper(helper.NewHash(helper.NewUUID().Bytes()).Md5().String())
+	tmp.DrawString(font, color.RGBA{98, 98, 98, 255}, t, 6)*/
+
+	draw.Draw(img, tmp.Bounds(), tmp, image.ZP, draw.Over)
+}
+
+// Create 生成一个验证码图片
+func (c *Captcha) Create(num int, t StrType) (*Image, string) {
+	if num <= 0 {
+		num = 4
+	}
+	dst := NewImage(c.size.X, c.size.Y)
+	//tmp := NewImage(c.size.X, c.size.Y)
+	c.drawBkg(dst)
+	c.drawNoises(dst)
+
+	str := string(c.randStr(num, int(t)))
+	c.drawString(dst, str)
+
+	/*uuid := helper.NewUUID().String()
+	c.drawString(dst, uuid)*/
+
+	return dst, str
+}
+
+func (c *Captcha) CreateCustom(str string) *Image {
+	if len(str) == 0 {
+		str = "unkown"
+	}
+	dst := NewImage(c.size.X, c.size.Y)
+	c.drawBkg(dst)
+	c.drawNoises(dst)
+	c.drawString(dst, str)
+	return dst
+}
+
+var fontKinds = [][]int{{10, 48}, {26, 97}, {26, 65}}
+var letters = []byte("34578acdefghjkmnpqstwxyABCDEFGHJKMNPQRSVWXY")
+
+// 生成随机字符串
+// size 个数 kind 模式
+func (c *Captcha) randStr(size int, kind int) []byte {
+	ikind, result := kind, make([]byte, size)
+	isAll := kind > 2 || kind < 0
+	rand.Seed(time.Now().UnixNano())
+	for i := 0; i < size; i++ {
+		if isAll {
+			ikind = rand.Intn(3)
+		}
+		scope, base := fontKinds[ikind][0], fontKinds[ikind][1]
+		result[i] = uint8(base + rand.Intn(scope))
+		// 不易混淆字符模式:重新生成字符
+		if kind == 4 {
+			result[i] = letters[rand.Intn(len(letters))]
+		}
+	}
+	return result
+}

+ 224 - 0
go/gopath/src/fohow.com/apps/helper/captcha/draw.go

@@ -0,0 +1,224 @@
+package captcha
+
+import (
+	"github.com/golang/freetype"
+	"github.com/golang/freetype/truetype"
+	"image"
+	"image/color"
+	"image/draw"
+	"math"
+)
+
+// Image 图片
+type Image struct {
+	*image.RGBA
+}
+
+// NewImage 创建一个新的图片
+func NewImage(w, h int) *Image {
+	img := &Image{image.NewRGBA(image.Rect(0, 0, w, h))}
+	return img
+}
+
+func sign(x int) int {
+	if x > 0 {
+		return 1
+	}
+	return -1
+}
+
+// DrawLine 画直线
+// Bresenham算法(https://zh.wikipedia.org/zh-cn/布雷森漢姆直線演算法)
+// x1,y1 起点 x2,y2终点
+func (img *Image) DrawLine(x1, y1, x2, y2 int, c color.Color) {
+	dx, dy, flag := int(math.Abs(float64(x2-x1))),
+		int(math.Abs(float64(y2-y1))),
+		false
+	if dy > dx {
+		flag = true
+		x1, y1 = y1, x1
+		x2, y2 = y2, x2
+		dx, dy = dy, dx
+	}
+	ix, iy := sign(x2-x1), sign(y2-y1)
+	n2dy := dy * 2
+	n2dydx := (dy - dx) * 2
+	d := n2dy - dx
+	for x1 != x2 {
+		if d < 0 {
+			d += n2dy
+		} else {
+			y1 += iy
+			d += n2dydx
+		}
+		if flag {
+			img.Set(y1, x1, c)
+		} else {
+			img.Set(x1, y1, c)
+		}
+		x1 += ix
+	}
+}
+
+func (img *Image) drawCircle8(xc, yc, x, y int, c color.Color) {
+	img.Set(xc+x, yc+y, c)
+	img.Set(xc-x, yc+y, c)
+	img.Set(xc+x, yc-y, c)
+	img.Set(xc-x, yc-y, c)
+	img.Set(xc+y, yc+x, c)
+	img.Set(xc-y, yc+x, c)
+	img.Set(xc+y, yc-x, c)
+	img.Set(xc-y, yc-x, c)
+}
+
+// DrawCircle 画圆
+// xc,yc 圆心坐标 r 半径 fill是否填充颜色
+func (img *Image) DrawCircle(xc, yc, r int, fill bool, c color.Color) {
+	size := img.Bounds().Size()
+	// 如果圆在图片可见区域外,直接退出
+	if xc+r < 0 || xc-r >= size.X || yc+r < 0 || yc-r >= size.Y {
+		return
+	}
+	x, y, d := 0, r, 3-2*r
+	for x <= y {
+		if fill {
+			for yi := x; yi <= y; yi++ {
+				img.drawCircle8(xc, yc, x, yi, c)
+			}
+		} else {
+			img.drawCircle8(xc, yc, x, y, c)
+		}
+		if d < 0 {
+			d = d + 4*x + 6
+		} else {
+			d = d + 4*(x-y) + 10
+			y--
+		}
+		x++
+	}
+}
+
+// DrawString 写字
+func (img *Image) DrawString(font *truetype.Font, c color.Color, str string, fontsize float64) {
+	ctx := freetype.NewContext()
+	// default 72dpi
+	ctx.SetDst(img)
+	ctx.SetClip(img.Bounds())
+	ctx.SetSrc(image.NewUniform(c))
+	ctx.SetFontSize(fontsize)
+	ctx.SetFont(font)
+	// 写入文字的位置
+	pt := freetype.Pt(0, int(-fontsize/6)+ctx.PointToFixed(fontsize).Ceil())
+	ctx.DrawString(str, pt)
+}
+
+// Rotate 旋转
+func (img *Image) Rotate(angle float64) image.Image {
+	return new(rotate).Rotate(angle, img.RGBA).transformRGBA()
+}
+
+// 填充背景
+func (img *Image) FillBkg(c image.Image) {
+	draw.Draw(img, img.Bounds(), c, image.ZP, draw.Over)
+}
+
+// 水波纹, amplude=振幅, period=周期
+// copy from https://github.com/dchest/captcha/blob/master/image.go
+func (img *Image) distortTo(amplude float64, period float64) {
+	w := img.Bounds().Max.X
+	h := img.Bounds().Max.Y
+
+	oldm := img.RGBA
+
+	dx := 1.4 * math.Pi / period
+	for x := 0; x < w; x++ {
+		for y := 0; y < h; y++ {
+			xo := amplude * math.Sin(float64(y)*dx)
+			yo := amplude * math.Cos(float64(x)*dx)
+			rgba := oldm.RGBAAt(x+int(xo), y+int(yo))
+			if rgba.A > 0 {
+				oldm.SetRGBA(x, y, rgba)
+			}
+		}
+	}
+}
+
+func inBounds(b image.Rectangle, x, y float64) bool {
+	if x < float64(b.Min.X) || x >= float64(b.Max.X) {
+		return false
+	}
+	if y < float64(b.Min.Y) || y >= float64(b.Max.Y) {
+		return false
+	}
+	return true
+}
+
+type rotate struct {
+	dx   float64
+	dy   float64
+	sin  float64
+	cos  float64
+	neww float64
+	newh float64
+	src  *image.RGBA
+}
+
+func radian(angle float64) float64 {
+	return angle * math.Pi / 180.0
+}
+
+func (r *rotate) Rotate(angle float64, src *image.RGBA) *rotate {
+	r.src = src
+	srsize := src.Bounds().Size()
+	width, height := srsize.X, srsize.Y
+
+	// 源图四个角的坐标(以图像中心为坐标系原点)
+	// 左下角,右下角,左上角,右上角
+	srcwp, srchp := float64(width)*0.5, float64(height)*0.5
+	srcx1, srcy1 := -srcwp, srchp
+	srcx2, srcy2 := srcwp, srchp
+	srcx3, srcy3 := -srcwp, -srchp
+	srcx4, srcy4 := srcwp, -srchp
+
+	r.sin, r.cos = math.Sincos(radian(angle))
+	// 旋转后的四角坐标
+	desx1, desy1 := r.cos*srcx1+r.sin*srcy1, -r.sin*srcx1+r.cos*srcy1
+	desx2, desy2 := r.cos*srcx2+r.sin*srcy2, -r.sin*srcx2+r.cos*srcy2
+	desx3, desy3 := r.cos*srcx3+r.sin*srcy3, -r.sin*srcx3+r.cos*srcy3
+	desx4, desy4 := r.cos*srcx4+r.sin*srcy4, -r.sin*srcx4+r.cos*srcy4
+
+	// 新的高度很宽度
+	r.neww = math.Max(math.Abs(desx4-desx1), math.Abs(desx3-desx2)) + 0.5
+	r.newh = math.Max(math.Abs(desy4-desy1), math.Abs(desy3-desy2)) + 0.5
+	r.dx = -0.5*r.neww*r.cos - 0.5*r.newh*r.sin + srcwp
+	r.dy = 0.5*r.neww*r.sin - 0.5*r.newh*r.cos + srchp
+	return r
+}
+
+func (r *rotate) pt(x, y int) (float64, float64) {
+	return float64(-y)*r.sin + float64(x)*r.cos + r.dy,
+		float64(y)*r.cos + float64(x)*r.sin + r.dx
+}
+
+func (r *rotate) transformRGBA() image.Image {
+
+	srcb := r.src.Bounds()
+	b := image.Rect(0, 0, int(r.neww), int(r.newh))
+	dst := image.NewRGBA(b)
+
+	for y := b.Min.Y; y < b.Max.Y; y++ {
+		for x := b.Min.X; x < b.Max.X; x++ {
+			sx, sy := r.pt(x, y)
+			if inBounds(srcb, sx, sy) {
+				// 消除锯齿填色
+				c := bili.RGBA(r.src, sx, sy)
+				off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
+				dst.Pix[off+0] = c.R
+				dst.Pix[off+1] = c.G
+				dst.Pix[off+2] = c.B
+				dst.Pix[off+3] = c.A
+			}
+		}
+	}
+	return dst
+}

+ 60 - 0
go/gopath/src/fohow.com/apps/helper/hash.go

@@ -0,0 +1,60 @@
+package helper
+
+import (
+	"crypto/md5"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/sha512"
+	"fmt"
+)
+
+type Hash struct {
+	data []byte
+}
+type HashData struct {
+	hash interface{}
+}
+
+func NewHashData(hash interface{}) *HashData {
+	return &HashData{hash: hash}
+}
+func NewHash(data []byte) *Hash {
+	return &Hash{data: data}
+}
+
+//Md5
+func (d *Hash) Md5() *HashData {
+	return NewHashData(md5.Sum(d.data))
+}
+
+//Sha1
+func (d *Hash) Sha1() *HashData {
+	return NewHashData(sha1.Sum(d.data))
+}
+
+//Sha256
+func (d *Hash) Sha256() *HashData {
+	return NewHashData(sha256.Sum256(d.data))
+}
+
+//Sha512
+func (d *Hash) Sha512() *HashData {
+	return NewHashData(sha512.Sum512(d.data))
+}
+
+//返回加密byte
+func (d *HashData) Result() interface{} {
+	return d.hash
+}
+
+//返回加密字符
+func (d *HashData) String() (s string) {
+	switch v := d.hash.(type) {
+	case [16]byte, [20]byte, [32]byte, [64]byte:
+		s = fmt.Sprintf("%x", v)
+	default:
+		s = ""
+	}
+	return s
+
+}

Разница между файлами не показана из-за своего большого размера
+ 151 - 0
go/gopath/src/fohow.com/apps/helper/idcard.go


+ 93 - 0
go/gopath/src/fohow.com/apps/helper/ip.go

@@ -0,0 +1,93 @@
+package helper
+
+import (
+	"github.com/kayon/iploc"
+	ip2 "lianjia/app/helper/ip"
+	"net"
+)
+
+//返回IP位置
+func GetIPCNInfo(ip string) (address string) {
+	IP := net.ParseIP(ip)
+	if IsPublicIP(IP) {
+		if IP.To4() != nil {
+			db, err := iploc.OpenWithoutIndexes("config/ip/qqwry.dat")
+			if err == nil {
+				defer db.Close()
+				address = db.Find(IP.String()).String()
+			}
+		}
+	} else {
+		address = "局域网"
+	}
+
+	return
+}
+
+//返回IP所在国家
+func GetIPCountry(ip string) (country string) {
+	IP := net.ParseIP(ip)
+	if IsPublicIP(IP) {
+		if IP.To4() != nil {
+			country = GetIP4Country(IP.String())
+		}
+		if IP.To16() != nil {
+			country = GetIP6Country(IP.String())
+		}
+	}
+	return
+}
+
+//返回IP4所在国家
+func GetIP4Country(ip string) (country string) {
+	db, err := ip2.OpenDB("config/ip/IP2LOCATION-LITE-DB1.BIN")
+	if err == nil {
+		defer db.Close()
+		results, err := db.Get_country_short(ip)
+		if err == nil {
+			country = results.Country_short
+		}
+	}
+	return
+}
+
+//返回IP6所在国家
+func GetIP6Country(ip string) (country string) {
+	db, err := ip2.OpenDB("config/ip/IP2LOCATION-LITE-DB1.IPV6.BIN")
+	if err == nil {
+		defer db.Close()
+		results, err := db.Get_country_short(ip)
+		if err == nil {
+			country = results.Country_short
+		}
+	}
+	return
+}
+
+func IsCNIp(ip string) bool {
+	IP := net.ParseIP(ip)
+	if IsPublicIP(IP) {
+		if GetIPCountry(IP.String()) != "CN" {
+			return false
+		}
+	}
+	return true
+}
+func IsPublicIP(IP net.IP) bool {
+	if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() {
+		return false
+	}
+	if ip4 := IP.To4(); ip4 != nil {
+		switch true {
+		case ip4[0] == 10:
+			return false
+		case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
+			return false
+		case ip4[0] == 192 && ip4[1] == 168:
+			return false
+		default:
+			return true
+		}
+	}
+	return false
+}

Разница между файлами не показана из-за своего большого размера
+ 1136 - 0
go/gopath/src/fohow.com/apps/helper/ip/ip2location.go


+ 146 - 0
go/gopath/src/fohow.com/apps/helper/math.go

@@ -0,0 +1,146 @@
+package helper
+
+import (
+	"errors"
+	"math"
+	"math/rand"
+	"strconv"
+	"time"
+)
+
+type Math struct {
+}
+
+func NewMath() *Math {
+	return &Math{}
+}
+
+//返回绝对值
+func (s *Math) Abs(number float64) float64 {
+	return math.Abs(number)
+}
+
+//返回随机整数
+func (s *Math) Rand(min int, max int) (int, error) {
+	if min > max {
+		return 0, errors.New("min: min cannot be greater than max")
+	}
+	if int31 := 1<<31 - 1; max > int31 {
+		return 0, errors.New("max: max can not be greater than " + strconv.Itoa(int31))
+	}
+	if min == max {
+		return min, nil
+	}
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	return r.Intn(max+1-min) + min, nil
+}
+
+//对浮点数进行四舍五入
+func (s *Math) Round(value float64) float64 {
+	return math.Floor(value + 0.5)
+}
+
+//向下舍入为最接近的整数
+func (s *Math) Floor(value float64) float64 {
+	return math.Floor(value)
+}
+
+//向上舍入为最接近的整数
+func (s *Math) Ceil(value float64) float64 {
+	return math.Ceil(value)
+}
+
+//返回圆周率的值
+func (s *Math) Pi() float64 {
+	return math.Pi
+}
+
+//返回最大值
+func (s *Math) Max(nums ...float64) (float64, error) {
+	if len(nums) < 2 {
+		return 0, errors.New("nums: the nums length is less than 2")
+	}
+	max := nums[0]
+	for i := 1; i < len(nums); i++ {
+		max = math.Max(max, nums[i])
+	}
+	return max, nil
+}
+
+//返回最小值
+func (s *Math) Min(nums ...float64) (float64, error) {
+	if len(nums) < 2 {
+		return 0, errors.New("nums: the nums length is less than 2")
+	}
+	min := nums[0]
+	for i := 1; i < len(nums); i++ {
+		min = math.Min(min, nums[i])
+	}
+	return min, nil
+}
+
+//十进制转换为二进制
+func (s *Math) Decbin(number int64) string {
+	return strconv.FormatInt(number, 2)
+}
+
+//二进制转换为十进制
+func (s *Math) Bindec(str string) (string, error) {
+	i, err := strconv.ParseInt(str, 2, 0)
+	if err != nil {
+		return "", err
+	}
+	return strconv.FormatInt(i, 10), nil
+}
+
+//十六进制值转换为 ASCII 字符
+func (s *Math) Hex2bin(data string) (string, error) {
+	i, err := strconv.ParseInt(data, 16, 0)
+	if err != nil {
+		return "", err
+	}
+	return strconv.FormatInt(i, 2), nil
+}
+
+//ASCII 字符的字符串转换为十六进制值
+func (s *Math) Bin2hex(str string) (string, error) {
+	i, err := strconv.ParseInt(str, 2, 0)
+	if err != nil {
+		return "", err
+	}
+	return strconv.FormatInt(i, 16), nil
+}
+
+//十进制转换为十六进制
+func (s *Math) Dechex(number int64) string {
+	return strconv.FormatInt(number, 16)
+}
+
+//十六进制转换为十进制
+func (s *Math) Hexdec(str string) (int64, error) {
+	return strconv.ParseInt(str, 16, 0)
+}
+
+//十进制转换为八进制
+func (s *Math) Decoct(number int64) string {
+	return strconv.FormatInt(number, 8)
+}
+
+//八进制转换为十进制
+func (s *Math) Octdec(str string) (int64, error) {
+	return strconv.ParseInt(str, 8, 0)
+}
+
+//任意进制之间转换数字
+func (s *Math) BaseConvert(number string, frombase, tobase int) (string, error) {
+	i, err := strconv.ParseInt(number, frombase, 0)
+	if err != nil {
+		return "", err
+	}
+	return strconv.FormatInt(i, tobase), nil
+}
+
+//判断是否为合法数值
+func (s *Math) IsNan(val float64) bool {
+	return math.IsNaN(val)
+}

+ 166 - 0
go/gopath/src/fohow.com/apps/helper/mime.go

@@ -0,0 +1,166 @@
+package helper
+
+var MimeExt = map[string]string{
+	"image/jpg":                   "jpg",
+	"image/jpeg":                  "jpeg",
+	"image/png":                   "png",
+	"image/gif":                   "gif",
+	"image/webp":                  "webp",
+	"image/x-canon-cr2":           "cr2",
+	"image/tiff":                  "tif",
+	"image/bmp":                   "bmp",
+	"image/heif":                  "heif",
+	"image/vnd.ms-photo":          "jxr",
+	"image/vnd.adobe.photoshop":   "psd",
+	"image/vnd.microsoft.icon":    "ico",
+	"image/vnd.dwg":               "dwg",
+	"video/mp4":                   "mp4",
+	"video/x-m4v":                 "m4v",
+	"video/x-matroska":            "mkv",
+	"video/webm":                  "webm",
+	"video/quicktime":             "mov",
+	"video/x-msvideo":             "avi",
+	"video/x-ms-wmv":              "wmv",
+	"video/mpeg":                  "mpg",
+	"video/x-flv":                 "flv",
+	"video/3gpp":                  "3gp",
+	"audio/midi":                  "mid",
+	"audio/mpeg":                  "mp3",
+	"audio/m4a":                   "m4a",
+	"audio/ogg":                   "ogg",
+	"audio/x-flac":                "flac",
+	"audio/x-wav":                 "wav",
+	"audio/amr":                   "amr",
+	"audio/aac":                   "aac",
+	"application/epub+zip":        "epub",
+	"application/zip":             "zip",
+	"application/x-tar":           "tar",
+	"application/vnd.rar":         "rar",
+	"application/gzip":            "gz",
+	"application/x-bzip2":         "bz2",
+	"application/x-7z-compressed": "7z",
+	"application/x-xz":            "xz",
+	"application/pdf":             "pdf",
+	"application/vnd.microsoft.portable-executable": "exe",
+	"application/x-shockwave-flash":                 "swf",
+	"application/rtf":                               "rtf",
+	"application/x-iso9660-image":                   "iso",
+	"application/octet-stream":                      "eot",
+	"application/postscript":                        "ps",
+	"application/vnd.sqlite3":                       "sqlite",
+	"application/x-nintendo-nes-rom":                "nes",
+	"application/x-google-chrome-extension":         "crx",
+	"application/vnd.ms-cab-compressed":             "cab",
+	"application/vnd.debian.binary-package":         "deb",
+	"application/x-unix-archive":                    "ar",
+	"application/x-compress":                        "Z",
+	"application/x-lzip":                            "lz",
+	"application/x-rpm":                             "rpm",
+	"application/x-executable":                      "elf",
+	"application/dicom":                             "dcm",
+	"application/msword":                            "doc",
+	"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
+	"application/vnd.ms-excel": "xls",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":         "xlsx",
+	"application/vnd.ms-powerpoint":                                             "ppt",
+	"application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
+}
+
+var ImageMime = map[string]string{
+	"jpg":  "image/jpg",
+	"jpeg": "image/jpeg",
+	"png":  "image/png",
+	"gif":  "image/gif",
+	"webp": "image/webp",
+	"cr2":  "image/x-canon-cr2",
+	"tif":  "image/tiff",
+	"bmp":  "image/bmp",
+	"heif": "image/heif",
+	"jxr":  "image/vnd.ms-photo",
+	"psd":  "image/vnd.adobe.photoshop",
+	"ico":  "image/vnd.microsoft.icon",
+	"dwg":  "image/vnd.dwg",
+}
+var VideoMime = map[string]string{
+	"mp4":  "video/mp4",
+	"m4v":  "video/x-m4v",
+	"mkv":  "video/x-matroska",
+	"webm": "video/webm",
+	"mov":  "video/quicktime",
+	"avi":  "video/x-msvideo",
+	"wmv":  "video/x-ms-wmv",
+	"mpg":  "video/mpeg",
+	"flv":  "video/x-flv",
+	"3gp":  "video/3gpp",
+}
+var AudioMime = map[string]string{
+	"mid":  "audio/midi",
+	"mp3":  "audio/mpeg",
+	"m4a":  "audio/m4a",
+	"ogg":  "audio/ogg",
+	"flac": "audio/x-flac",
+	"wav":  "audio/x-wav",
+	"amr":  "audio/amr",
+	"aac":  "audio/aac",
+}
+var DocumentMime = map[string]string{
+	"dcm":  "application/dicom",
+	"doc":  "application/msword",
+	"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+	"xls":  "application/vnd.ms-excel",
+	"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+	"ppt":  "application/vnd.ms-powerpoint",
+	"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+}
+var ArchiveMime = map[string]string{
+	"epub":   "application/epub+zip",
+	"zip":    "application/zip",
+	"tar":    "application/x-tar",
+	"rar":    "application/vnd.rar",
+	"gz":     "application/gzip",
+	"bz2":    "application/x-bzip2",
+	"7z":     "application/x-7z-compressed",
+	"xz":     "application/x-xz",
+	"pdf":    "application/pdf",
+	"exe":    "application/vnd.microsoft.portable-executable",
+	"swf":    "application/x-shockwave-flash",
+	"rtf":    "application/rtf",
+	"iso":    "application/x-iso9660-image",
+	"eot":    "application/octet-stream",
+	"ps":     "application/postscript",
+	"sqlite": "application/vnd.sqlite3",
+	"nes":    "application/x-nintendo-nes-rom",
+	"crx":    "application/x-google-chrome-extension",
+	"cab":    "application/vnd.ms-cab-compressed",
+	"deb":    "application/vnd.debian.binary-package",
+	"ar":     "application/x-unix-archive",
+	"Z":      "application/x-compress",
+	"lz":     "application/x-lzip",
+	"rpm":    "application/x-rpm",
+	"elf":    "application/x-executable",
+}
+
+const TYPE_IMAGE = "image"
+const TYPE_VIDEO = "video"
+const TYPE_AUDIO = "audio"
+const TYPE_ARCHIVE = "archive"
+const TYPE_DOCUMENTS = "document"
+
+func GetMime(e string, t string) (mime string, ok bool) {
+	if t == TYPE_VIDEO {
+		mime, ok = VideoMime[e]
+	} else if t == TYPE_AUDIO {
+		mime, ok = AudioMime[e]
+	} else if t == TYPE_ARCHIVE {
+		mime, ok = ArchiveMime[e]
+	} else if t == TYPE_DOCUMENTS {
+		mime, ok = DocumentMime[e]
+	} else if t == TYPE_IMAGE {
+		mime, ok = ImageMime[e]
+	}
+	return
+}
+func GetExt(m string) (ext string, ok bool) {
+	ext, ok = MimeExt[m]
+	return
+}

+ 147 - 0
go/gopath/src/fohow.com/apps/helper/random.go

@@ -0,0 +1,147 @@
+package helper
+
+import (
+	"encoding/hex"
+	"math"
+	"math/rand"
+	"time"
+)
+
+type Random struct {
+	rand *rand.Rand
+}
+
+type RandomSpec struct {
+	Value []rune
+}
+
+func NewRandomSpec(value []rune) *RandomSpec {
+	return &RandomSpec{Value: value}
+}
+
+type UUID struct {
+	bytes []byte
+}
+
+//生成UUID
+func NewUUID() *UUID {
+	bytes := make([]byte, 16)
+	rand.Read(bytes)
+	bytes[6] = byte(0x40 | (int(bytes[6]) & 0xf))
+	bytes[8] = byte(0x80 | (int(bytes[8]) & 0x3f))
+	return &UUID{bytes: bytes}
+}
+
+func NewRandom() *Random {
+	return &Random{rand.New(rand.NewSource(time.Now().UnixNano()))}
+}
+func (v *RandomSpec) Result() *Result {
+	return NewResult(v.String())
+}
+
+//返回随机数为字符
+func (s *RandomSpec) String() string {
+	return string(s.Value)
+}
+
+//返回UUID为字符
+func (uuid *UUID) String() string {
+	str := hex.EncodeToString(uuid.bytes)
+	return str[:8] + "-" + str[8:12] + "-" + str[12:16] + "-" + str[16:20] + "-" + str[20:]
+}
+func (uuid *UUID) Result() *Result {
+	return NewResult(uuid.String())
+}
+
+//返回UUID为Bytes
+func (uuid *UUID) Bytes() []byte {
+	val := make([]byte, 16)
+	copy(val, uuid.bytes)
+	return val
+}
+
+//生成指定区间随机数
+func (r Random) RandInt(min, max int) int {
+	if min >= max || min == 0 || max == 0 {
+		return max
+	}
+	return r.Intn(max-min) + min
+}
+
+//随机数
+func (r Random) Intn(size int) int {
+	return r.rand.Intn(size)
+}
+
+//随机字母
+func (r Random) Alpha(size int) *RandomSpec {
+	return NewRandomSpec(r.Spec(size, 0, 0, true, false, nil))
+}
+
+//随机字母和数字
+func (r Random) AlphaNum(size int) *RandomSpec {
+	return NewRandomSpec(r.Spec(size, 0, 0, true, true, nil))
+}
+
+//随机数字
+func (r Random) Num(size int) *RandomSpec {
+	return NewRandomSpec(r.Spec(size, 0, 0, false, true, nil))
+}
+
+//随机Ascii
+func (r Random) Ascii(size int) *RandomSpec {
+	return NewRandomSpec(r.Spec(size, 32, 127, false, false, nil))
+}
+
+func (r Random) Spec(count int, start, end int, letters, numbers bool, chars []rune) []rune {
+	if count == 0 {
+		return nil
+	}
+	if start == 0 && end == 0 {
+		end = 'z' + 1
+		start = ' '
+		if !letters && !numbers {
+			start = 0
+			end = math.MaxInt32
+		}
+	}
+	buffer := make([]rune, count)
+	gap := end - start
+	for count != 0 {
+		count--
+		var ch rune
+		if len(chars) == 0 {
+			ch = rune(r.Intn(gap) + start)
+		} else {
+			ch = chars[r.Intn(gap)+start]
+		}
+		if letters && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) ||
+			numbers && (ch >= '0' && ch <= '9') ||
+			(!letters && !numbers) {
+			if ch >= rune(56320) && ch <= rune(57343) {
+				if count == 0 {
+					count++
+				} else {
+					buffer[count] = ch
+					count--
+					buffer[count] = rune(55296 + r.Intn(128))
+				}
+			} else if ch >= rune(55296) && ch <= rune(56191) {
+				if count == 0 {
+					count++
+				} else {
+					buffer[count] = rune(56320 + r.Intn(128))
+					count--
+					buffer[count] = ch
+				}
+			} else if ch >= rune(56192) && ch <= rune(56319) {
+				count++
+			} else {
+				buffer[count] = ch
+			}
+		} else {
+			count++
+		}
+	}
+	return buffer
+}

+ 113 - 0
go/gopath/src/fohow.com/apps/helper/raw.go

@@ -0,0 +1,113 @@
+package helper
+
+import (
+	"encoding/json"
+	"fmt"
+	"reflect"
+	"strconv"
+)
+
+type Raw struct {
+	val interface{}
+}
+type argInt []int
+
+func NewRaw(val interface{}) *Raw {
+	return &Raw{val: val}
+}
+
+//json解码
+func (v *Raw) JSONDecode(val interface{}) error {
+	return json.Unmarshal(v.val.([]byte), val)
+}
+
+//json加密
+func (v *Raw) JSONEncode() ([]byte, error) {
+	j, err := json.Marshal(v.val)
+	if err == nil {
+		return j, nil
+	}
+	return nil, err
+}
+func (a argInt) Get(i int, args ...int) (r int) {
+	if i >= 0 && i < len(a) {
+		r = a[i]
+	}
+	if len(args) > 0 {
+		r = args[0]
+	}
+	return r
+}
+
+func (v *Raw) Result(args ...int) *Result {
+	return NewResult(v.ToStr(args...))
+}
+
+func (v *Raw) String(args ...int) (s string) {
+	return v.ToStr(args...)
+}
+func (v *Raw) Str(args ...int) *Str {
+	return NewStr(v.ToStr(args...))
+}
+
+//是否为空
+func (r *Raw) Empty() bool {
+	if r.val == nil {
+		return true
+	}
+	v := reflect.ValueOf(r.val)
+	switch v.Kind() {
+	case reflect.String, reflect.Array:
+		return v.Len() == 0
+	case reflect.Map, reflect.Slice:
+		return v.Len() == 0 || v.IsNil()
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	}
+	return reflect.DeepEqual(r.val, reflect.Zero(v.Type()).Interface())
+}
+func (v *Raw) ToStr(args ...int) (s string) {
+	switch v := v.val.(type) {
+	case bool:
+		s = strconv.FormatBool(v)
+	case float32:
+		s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
+	case float64:
+		s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
+	case int:
+		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
+	case int8:
+		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
+	case int16:
+		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
+	case int32:
+		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
+	case int64:
+		s = strconv.FormatInt(v, argInt(args).Get(0, 10))
+	case uint:
+		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
+	case uint8:
+		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
+	case uint16:
+		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
+	case uint32:
+		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
+	case uint64:
+		s = strconv.FormatUint(v, argInt(args).Get(0, 10))
+	case string:
+		s = v
+	case []byte:
+		s = string(v)
+	default:
+		s = fmt.Sprintf("%v", v)
+	}
+	return s
+}

+ 221 - 0
go/gopath/src/fohow.com/apps/helper/result.go

@@ -0,0 +1,221 @@
+package helper
+
+import (
+	"reflect"
+	"strings"
+)
+
+type Result struct {
+	val interface{}
+}
+
+func NewResult(val interface{}) *Result {
+	return &Result{val: val}
+}
+
+func (r *Result) String(defaultval ...string) string {
+	switch val := r.val.(type) {
+	case string:
+		return val
+	default:
+		for k, v := range defaultval {
+			if k == 0 {
+				return v
+			}
+		}
+		return ""
+	}
+}
+
+func (r *Result) Val() interface{} {
+	return r.val
+}
+
+func (r *Result) Result() interface{} {
+	return r.val
+}
+
+func (r *Result) Int(defaultval ...int) int {
+	switch val := r.val.(type) {
+	case int:
+		return val
+	case string:
+		return NewStr(val).ToInt()
+	default:
+		for k, v := range defaultval {
+			if k == 0 {
+				return v
+			}
+		}
+		return 0
+	}
+}
+
+func (r *Result) Int64(defaultval ...int64) int64 {
+	switch val := r.val.(type) {
+	case int64:
+		return val
+	case string:
+		return NewStr(val).ToInt64()
+	default:
+		for k, v := range defaultval {
+			if k == 0 {
+				return v
+			}
+		}
+		return 0
+	}
+}
+
+func (r *Result) Uint64(defaultval ...uint64) uint64 {
+	switch val := r.val.(type) {
+	case uint64:
+		return val
+	case string:
+		return NewStr(val).ToUint64()
+	default:
+		for k, v := range defaultval {
+			if k == 0 {
+				return v
+			}
+		}
+		return 0
+	}
+}
+
+func (r *Result) Float32(defaultval ...float32) float32 {
+	switch val := r.val.(type) {
+	case float32:
+		return val
+	case string:
+		return NewStr(val).ToFloat32()
+	default:
+		for k, v := range defaultval {
+			if k == 0 {
+				return v
+			}
+		}
+		return 0
+	}
+}
+
+func (r *Result) Float64(defaultval ...float64) float64 {
+	switch val := r.val.(type) {
+	case float64:
+		return val
+	case string:
+		return NewStr(val).ToFloat64()
+	default:
+		for k, v := range defaultval {
+			if k == 0 {
+				return v
+			}
+		}
+		return 0
+	}
+}
+
+func (r *Result) Bool(defaultval ...bool) bool {
+	switch val := r.val.(type) {
+	case bool:
+		return val
+	case string:
+		return NewStr(val).ToBool()
+	default:
+		for k, v := range defaultval {
+			if k == 0 {
+				return v
+			}
+		}
+		return false
+	}
+}
+
+func (r *Result) Bytes(defaultval ...[]byte) []byte {
+	switch val := r.val.(type) {
+	case []byte:
+		return val
+	case string:
+		return NewStr(val).ToBytes()
+	default:
+		for k, v := range defaultval {
+			if k == 0 {
+				return v
+			}
+		}
+		return nil
+	}
+}
+
+//是否为空
+func (r *Result) Empty() bool {
+	if r.val == nil {
+		return true
+	}
+	v := reflect.ValueOf(r.val)
+	switch v.Kind() {
+	case reflect.String, reflect.Array:
+		return v.Len() == 0
+	case reflect.Map, reflect.Slice:
+		return v.Len() == 0 || v.IsNil()
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	}
+	return reflect.DeepEqual(r.val, reflect.Zero(v.Type()).Interface())
+}
+
+//判断是否为数字
+func (r *Result) IsNumeric() bool {
+	switch r.val.(type) {
+	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+		return true
+	case float32, float64, complex64, complex128:
+		return true
+	case string:
+		str := r.val.(string)
+		if str == "" {
+			return false
+		}
+		str = strings.TrimSpace(str)
+		if str[0] == '-' || str[0] == '+' {
+			if len(str) == 1 {
+				return false
+			}
+			str = str[1:]
+		}
+		if len(str) > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') {
+			for _, h := range str[2:] {
+				if !((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') || (h >= 'A' && h <= 'F')) {
+					return false
+				}
+			}
+			return true
+		}
+		p, s, l := 0, 0, len(str)
+		for i, v := range str {
+			if v == '.' {
+				if p > 0 || s > 0 || i+1 == l {
+					return false
+				}
+				p = i
+			} else if v == 'e' || v == 'E' {
+				if i == 0 || s > 0 || i+1 == l {
+					return false
+				}
+				s = i
+			} else if v < '0' || v > '9' {
+				return false
+			}
+		}
+		return true
+	}
+	return false
+}

+ 281 - 0
go/gopath/src/fohow.com/apps/helper/slice.go

@@ -0,0 +1,281 @@
+package helper
+
+import (
+	"errors"
+	"math"
+	"math/rand"
+	"reflect"
+	"strings"
+	"time"
+)
+
+type Slice struct {
+}
+
+func NewSlice() *Slice {
+	return &Slice{}
+}
+
+//用给定的值填充切片
+func (slice *Slice) Fill(startIndex int, num uint, value interface{}) map[int]interface{} {
+	m := make(map[int]interface{})
+	var i uint
+	for i = 0; i < num; i++ {
+		m[startIndex] = value
+		startIndex++
+	}
+	return m
+}
+
+//交换切片中的键和值
+func (slice *Slice) Flip(m map[interface{}]interface{}) map[interface{}]interface{} {
+	n := make(map[interface{}]interface{})
+	for i, v := range m {
+		n[v] = i
+	}
+	return n
+}
+
+//返回切片的键名
+func (slice *Slice) Keys(elements map[interface{}]interface{}) []interface{} {
+	i, keys := 0, make([]interface{}, len(elements))
+	for key := range elements {
+		keys[i] = key
+		i++
+	}
+	return keys
+}
+
+//返回切片中所有的值
+func (slice *Slice) Values(elements map[interface{}]interface{}) []interface{} {
+	i, vals := 0, make([]interface{}, len(elements))
+	for _, val := range elements {
+		vals[i] = val
+		i++
+	}
+	return vals
+}
+
+//合并一个或多个切片
+func (slice *Slice) Merge(ss ...[]interface{}) []interface{} {
+	n := 0
+	for _, v := range ss {
+		n += len(v)
+	}
+	s := make([]interface{}, 0, n)
+	for _, v := range ss {
+		s = append(s, v...)
+	}
+	return s
+}
+
+//将一个切片分割成多个
+func (slice *Slice) Chunk(s []interface{}, size int) ([][]interface{}, error) {
+	if size < 1 {
+		return nil, errors.New("size: cannot be less than 1")
+	}
+	length := len(s)
+	chunks := int(math.Ceil(float64(length) / float64(size)))
+	var n [][]interface{}
+	for i, end := 0, 0; chunks > 0; chunks-- {
+		end = (i + 1) * size
+		if end > length {
+			end = length
+		}
+		n = append(n, s[i*size:end])
+		i++
+	}
+	return n, nil
+}
+
+//以指定长度将一个值填充进切片
+func (slice *Slice) Pad(s []interface{}, size int, val interface{}) []interface{} {
+	if size == 0 || (size > 0 && size < len(s)) || (size < 0 && size > -len(s)) {
+		return s
+	}
+	n := size
+	if size < 0 {
+		n = -size
+	}
+	n -= len(s)
+	tmp := make([]interface{}, n)
+	for i := 0; i < n; i++ {
+		tmp[i] = val
+	}
+	if size > 0 {
+		return append(s, tmp...)
+	}
+	return append(tmp, s...)
+}
+
+//从切片中取出一段
+func (slice *Slice) Slice(s []interface{}, offset, length uint) ([]interface{}, error) {
+	if offset > uint(len(s)) {
+		return nil, errors.New("offset: the offset is less than the length of s")
+	}
+	end := offset + length
+	if end < uint(len(s)) {
+		return s[offset:end], nil
+	} else {
+		return s[offset:], nil
+	}
+}
+
+//从切片中随机取出一个或多个单元
+func (slice *Slice) Rand(elements []interface{}) []interface{} {
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	n := make([]interface{}, len(elements))
+	for i, v := range r.Perm(len(elements)) {
+		n[i] = elements[v]
+	}
+	return n
+}
+
+//返回切片中指定的一列
+func (slice *Slice) Column(input map[string]map[string]interface{}, columnKey string) []interface{} {
+	columns := make([]interface{}, 0, len(input))
+	for _, val := range input {
+		if v, ok := val[columnKey]; ok {
+			columns = append(columns, v)
+		}
+	}
+	return columns
+}
+
+//将一个或多个单元压入切片的末尾(入栈)
+func (slice *Slice) Push(s *[]interface{}, elements ...interface{}) int {
+	*s = append(*s, elements...)
+	return len(*s)
+}
+
+// 弹出切片最后一个单元(出栈)
+func (slice *Slice) Pop(s *[]interface{}) interface{} {
+	if len(*s) == 0 {
+		return nil
+	}
+	ep := len(*s) - 1
+	e := (*s)[ep]
+	*s = (*s)[:ep]
+	return e
+}
+
+//在切片开头插入一个或多个单元
+func (slice *Slice) Unshift(s *[]interface{}, elements ...interface{}) int {
+	*s = append(elements, *s...)
+	return len(*s)
+}
+
+//将切片开头的单元移出切片
+func (slice *Slice) Shift(s *[]interface{}) interface{} {
+	if len(*s) == 0 {
+		return nil
+	}
+	f := (*s)[0]
+	*s = (*s)[1:]
+	return f
+}
+
+//检查切片里是否有指定的键名或索引
+func (slice *Slice) KeyExists(key interface{}, m map[interface{}]interface{}) bool {
+	_, ok := m[key]
+	return ok
+}
+
+//创建一个切片,用一个切片的值作为其键名,另一个切片的值作为其值
+func (slice *Slice) Combine(s1, s2 []interface{}) (map[interface{}]interface{}, error) {
+	if len(s1) != len(s2) {
+		return nil, errors.New("the number of elements for each slice isn't equal")
+	}
+	m := make(map[interface{}]interface{}, len(s1))
+	for i, v := range s1 {
+		m[v] = s2[i]
+	}
+	return m, nil
+}
+
+//返回单元顺序相反的切片
+func (slice *Slice) Reverse(s []interface{}) []interface{} {
+	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
+		s[i], s[j] = s[j], s[i]
+	}
+	return s
+}
+
+//将一个一维切片的值转化为字符串
+func (slice *Slice) Implode(glue string, pieces []string) string {
+	return slice.Join(glue, pieces)
+}
+func (slice *Slice) Join(glue string, pieces []string) string {
+	return strings.Join(pieces, glue)
+}
+
+//检查切片中是否存在某个值
+func (slice *Slice) InValue(needle interface{}, haystack interface{}) bool {
+	val := reflect.ValueOf(haystack)
+	switch val.Kind() {
+	case reflect.Slice, reflect.Array:
+		for i := 0; i < val.Len(); i++ {
+			if reflect.DeepEqual(needle, val.Index(i).Interface()) {
+				return true
+			}
+		}
+	case reflect.Map:
+		for _, k := range val.MapKeys() {
+			if reflect.DeepEqual(needle, val.MapIndex(k).Interface()) {
+				return true
+			}
+		}
+	default:
+		return false
+	}
+
+	return false
+}
+
+//判断操作键是否在切片
+func (slice *Slice) InKey(key interface{}, s map[interface{}]interface{}) (interface{}, bool) {
+	if v, ok := s[key]; ok {
+		return v, true
+	} else {
+		return nil, false
+	}
+}
+
+//获取操作键值 key支持多维如 a.b
+func (slice *Slice) GetValue(key string, s map[interface{}]interface{}) interface{} {
+	keys := strings.Split(key, ".")
+	tmp := s
+	for index, k := range keys {
+		if v, ok := slice.InKey(k, tmp); ok {
+			switch v.(type) {
+			case map[interface{}]interface{}:
+				{
+					tmp = v.(map[interface{}]interface{})
+					if index == len(keys)-1 {
+						return tmp
+					}
+				}
+			default:
+				{
+					return v
+				}
+			}
+		}
+	}
+	return nil
+}
+
+//获取type Name
+func (slice *Slice) GetTypeName(t interface{}) string {
+	return reflect.TypeOf(t).Name()
+}
+
+//获取type Field
+func (slice *Slice) GetTypeField(t interface{}) []reflect.StructField {
+	var f []reflect.StructField
+	of := reflect.TypeOf(t)
+	for i := 0; i < of.NumField(); i++ {
+		f = append(f, of.Field(i))
+	}
+	return f
+}

+ 401 - 0
go/gopath/src/fohow.com/apps/helper/str.go

@@ -0,0 +1,401 @@
+package helper
+
+import (
+	"math/big"
+	"strconv"
+	"strings"
+	"unicode"
+	"unsafe"
+)
+
+type Str struct {
+	value string
+}
+
+func NewStr(value string) *Str {
+	return &Str{value: value}
+}
+
+//设置字符
+func (s *Str) Set(v string) *Str {
+	if v != "" {
+		s.value = v
+	} else {
+		s.Clear()
+	}
+	return s
+}
+
+//将一个一维切片的值转化为字符串
+func (s *Str) Join(glue string, pieces []string) *Str {
+	s.value = NewSlice().Implode(glue, pieces)
+	return s
+}
+
+//移除字符串两侧的空白字符或其他预定义字符
+func (s *Str) Trim(characterMask ...string) *Str {
+	if len(characterMask) == 0 {
+		s.value = strings.TrimSpace(s.value)
+	} else {
+		s.value = strings.Trim(s.value, characterMask[0])
+	}
+	return s
+}
+
+//移除字符串左侧的空白字符或其他预定义字符
+func (s *Str) Ltrim(characterMask ...string) *Str {
+	if len(characterMask) == 0 {
+		s.value = strings.TrimLeftFunc(s.value, unicode.IsSpace)
+	} else {
+		s.value = strings.TrimLeft(s.value, characterMask[0])
+	}
+	return s
+}
+
+//移除字符串右侧的空白字符或其他预定义字符
+func (s *Str) Rtrim(characterMask ...string) *Str {
+	if len(characterMask) == 0 {
+		s.value = strings.TrimRightFunc(s.value, unicode.IsSpace)
+	} else {
+		s.value = strings.TrimRight(s.value, characterMask[0])
+	}
+	return s
+}
+
+//小写字母转大写
+func (s *Str) Upper() *Str {
+	s.value = strings.ToUpper(s.value)
+	return s
+}
+
+//大写字母转小写
+func (s *Str) Lower() *Str {
+	s.value = strings.ToLower(s.value)
+	return s
+}
+
+//首字符转换为大写
+func (s *Str) Ucfirst() *Str {
+	for _, v := range s.value {
+		u := string(unicode.ToUpper(v))
+		s.value = u + s.value[len(u):]
+	}
+	return s
+}
+
+//首字符转换为小写
+func (s *Str) Lcfirst() *Str {
+	for _, v := range s.value {
+		u := string(unicode.ToLower(v))
+		s.value = u + s.value[len(u):]
+	}
+	return s
+}
+
+//每个单词的首字符转换为大写
+func (s *Str) Ucwords() *Str {
+	s.value = strings.Title(s.value)
+	return s
+}
+
+//查找字符串在另一字符串中第一次出现的位置 对大小写敏感
+func (s *Str) Strpos(needle string, offset int) int {
+	length := len(s.value)
+	if length == 0 || offset > length || -offset > length {
+		return -1
+	}
+	if offset < 0 {
+		offset += length
+	}
+	pos := strings.Index(s.value[offset:], needle)
+	if pos == -1 {
+		return -1
+	}
+	return pos + offset
+}
+
+//查找字符串在另一字符串中第一次出现的位置(不区分大小写)
+func (s *Str) Stripos(needle string, offset int) int {
+	length := len(s.value)
+	if length == 0 || offset > length || -offset > length {
+		return -1
+	}
+
+	haystack := s.value[offset:]
+	if offset < 0 {
+		offset += length
+	}
+	pos := strings.Index(strings.ToLower(haystack), strings.ToLower(needle))
+	if pos == -1 {
+		return -1
+	}
+	return pos + offset
+}
+
+//查找字符串在另一字符串中最后一次出现的位置(区分大小写)
+func (s *Str) Strrpos(needle string, offset int) int {
+	pos, length := 0, len(s.value)
+	if length == 0 || offset > length || -offset > length {
+		return -1
+	}
+	var haystack string
+	if offset < 0 {
+		haystack = s.value[:offset+length+1]
+	} else {
+		haystack = s.value[offset:]
+	}
+	pos = strings.LastIndex(haystack, needle)
+	if offset > 0 && pos != -1 {
+		pos += offset
+	}
+	return pos
+}
+
+//查找字符串在另一字符串中最后一次出现的位置(不区分大小写)
+func (s *Str) Strripos(needle string, offset int) int {
+	pos, length := 0, len(s.value)
+	if length == 0 || offset > length || -offset > length {
+		return -1
+	}
+	var haystack string
+	if offset < 0 {
+		haystack = s.value[:offset+length+1]
+	} else {
+		haystack = s.value[offset:]
+	}
+	pos = strings.LastIndex(strings.ToLower(haystack), strings.ToLower(needle))
+	if offset > 0 && pos != -1 {
+		pos += offset
+	}
+	return pos
+}
+
+//字母下划线命名转换驼峰命名
+func (s *Str) Camel() *Str {
+	data := make([]byte, 0, len(s.value))
+	flag, num := true, len(s.value)-1
+	for i := 0; i <= num; i++ {
+		d := s.value[i]
+		if d == '_' {
+			flag = true
+			continue
+		} else if flag {
+			if d >= 'a' && d <= 'z' {
+				d = d - 32
+			}
+			flag = false
+		}
+		data = append(data, d)
+	}
+	s.value = string(data[:])
+	return s
+}
+
+//字母驼峰命名转换为下划线命名
+func (s *Str) Snake() *Str {
+	num := len(s.value)
+	data := make([]byte, 0, num*2)
+	j := false
+	for i := 0; i < num; i++ {
+		d := s.value[i]
+		if i > 0 && d >= 'A' && d <= 'Z' && j {
+			data = append(data, '_')
+		}
+		if d != '_' {
+			j = true
+		}
+		data = append(data, d)
+	}
+	s.value = string(data[:])
+	return s.Lower()
+}
+
+//将字符串转换为切片
+func (s *Str) ToBytes() []byte {
+	return *(*[]byte)(unsafe.Pointer(
+		&struct {
+			string
+			Cap int
+		}{s.value, len(s.value)},
+	))
+}
+
+//转换为Bool
+func (s *Str) ToBool() bool {
+	if b, err := strconv.ParseBool(s.String()); err == nil {
+		return b
+	}
+	return false
+}
+
+//转换为float32
+func (s *Str) ToFloat32() float32 {
+	if v, err := strconv.ParseFloat(s.String(), 32); err == nil {
+		return float32(v)
+	}
+	return 0
+}
+
+//转换为float64
+func (s *Str) ToFloat64() float64 {
+	if f, err := strconv.ParseFloat(s.String(), 64); err == nil {
+		return f
+	}
+	return 0
+}
+
+//转换为int
+func (s *Str) ToInt() int {
+	if i, err := strconv.ParseInt(s.String(), 10, 32); err == nil {
+		return int(i)
+	}
+	return 0
+}
+
+//转换为int8
+func (s *Str) ToInt8() int8 {
+	if i, err := strconv.ParseInt(s.String(), 10, 8); err == nil {
+		return int8(i)
+	}
+	return 0
+}
+
+//转换为int16
+func (s *Str) ToInt16() int16 {
+	if i, err := strconv.ParseInt(s.String(), 10, 16); err == nil {
+		return int16(i)
+	}
+	return 0
+}
+
+//转换为int32
+func (s *Str) ToInt32() int32 {
+	if i, err := strconv.ParseInt(s.String(), 10, 32); err == nil {
+		return int32(i)
+	}
+	return 0
+}
+
+//转换为int64
+func (s *Str) ToInt64() int64 {
+	v, err := strconv.ParseInt(s.String(), 10, 64)
+	if err != nil {
+		i := new(big.Int)
+		ni, ok := i.SetString(s.String(), 10)
+		if !ok {
+			return 0
+		}
+		return ni.Int64()
+	}
+	return v
+}
+
+//转换为uint
+func (s *Str) ToUint() uint {
+	if i, err := strconv.ParseUint(s.String(), 10, 32); err == nil {
+		return uint(i)
+	}
+	return 0
+}
+
+//转换为uint8
+func (s *Str) ToUint8() uint8 {
+	if i, err := strconv.ParseUint(s.String(), 10, 8); err == nil {
+		return uint8(i)
+	}
+	return 0
+}
+
+//转换为uint16
+func (s *Str) ToUint16() uint16 {
+	if i, err := strconv.ParseUint(s.String(), 10, 16); err == nil {
+		return uint16(i)
+	}
+	return 0
+}
+
+//转换为uint32
+func (s *Str) ToUint32() uint32 {
+	if i, err := strconv.ParseUint(s.String(), 10, 32); err == nil {
+		return uint32(i)
+	}
+	return 0
+}
+
+//Md5
+func (s *Str) ToMd5() *HashData {
+	return NewHash([]byte(s.value)).Md5()
+}
+
+//Sha1
+func (s *Str) ToSha1() *HashData {
+	return NewHash([]byte(s.value)).Sha1()
+}
+
+//Sha256
+func (s *Str) ToSha256() *HashData {
+	return NewHash([]byte(s.value)).Sha256()
+}
+
+//Sha512
+func (s *Str) ToSha512() *HashData {
+	return NewHash([]byte(s.value)).Sha512()
+}
+
+//转换为uint64
+func (s *Str) ToUint64() uint64 {
+	v, err := strconv.ParseUint(s.String(), 10, 64)
+	if err != nil {
+		i := new(big.Int)
+		ni, ok := i.SetString(s.String(), 10)
+		if !ok {
+			return 0
+		}
+		return ni.Uint64()
+	}
+	return v
+}
+
+//判断后缀
+func (s *Str) HasSuffix(suffix string) bool {
+	return strings.HasSuffix(s.value, suffix)
+}
+
+//判断前缀
+func (s *Str) HasPrefix(prefix string) bool {
+	return strings.HasSuffix(s.value, prefix)
+}
+
+//字符串分割
+func (s *Str) Explode(delimiter string) []string {
+	return strings.Split(s.value, delimiter)
+}
+
+func (s *Str) String() string {
+	if s.Exist() {
+		return s.value
+	}
+	return ""
+}
+
+//清除字符串
+func (s *Str) Clear() {
+	s.Set(string(0x1E))
+}
+
+//检查字符串是否存在
+func (s *Str) Exist() bool {
+	return s.value != string(0x1E)
+}
+
+//字符串组是否包含某字符串
+func StringsContains(array []string, val string) (index int) {
+	index = -1
+	for i := 0; i < len(array); i++ {
+		if array[i] == val {
+			index = i
+			return index
+		}
+	}
+	return index
+}

+ 161 - 0
go/gopath/src/fohow.com/apps/helper/time.go

@@ -0,0 +1,161 @@
+package helper
+
+import (
+	"fmt"
+	"github.com/araddon/dateparse"
+	"strings"
+	"time"
+)
+
+var TimeLoad, _ = time.LoadLocation("PRC")
+
+//当前Unix时间
+func Time() int64 {
+	return time.Now().Unix()
+}
+
+//将英文文本日期时间解析为 Unix 时间戳
+func Strtotime(format, strtime string) (int64, error) {
+	t, err := time.Parse(format, strtime)
+	if err != nil {
+		return 0, err
+	}
+	return t.Unix(), nil
+}
+
+// 格式化时间
+func Date(format string, timestamp int64) string {
+	return time.Unix(timestamp, 0).Format(format)
+}
+
+//检查时间是否合法
+func Checkdate(month int, day int, year int) bool {
+	if month < 1 || month > 12 || day < 1 || day > 31 || year < 1 || year > 32767 {
+		return false
+	}
+	switch month {
+	case 4, 6, 9, 11:
+		if day > 30 {
+			return false
+		}
+	case 2:
+		// leap year
+		if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
+			if day > 29 {
+				return false
+			}
+		} else if day > 28 {
+			return false
+		}
+	}
+	return true
+}
+
+func GetTime(value string) string {
+	var t time.Time
+	if value == "" {
+		t = time.Now().In(TimeLoad)
+	} else {
+		t = TimeParseAny(value)
+	}
+	return fmt.Sprintf("%02d", t.Hour()) + ":" + fmt.Sprintf("%02d", t.Minute()) + ":" + fmt.Sprintf("%02d", t.Second())
+}
+
+func GetDate(value string) string {
+	var t time.Time
+	if value == "" {
+		t = time.Now().In(TimeLoad)
+	} else {
+		t = TimeParseAny(value)
+	}
+	return fmt.Sprintf("%d", t.Year()) + "-" + fmt.Sprintf("%02d", t.Month()) + "-" + fmt.Sprintf("%02d", t.Day())
+}
+
+func GetDateMonth(value string) string {
+	var t time.Time
+	if value == "" {
+		t = time.Now().In(TimeLoad)
+	} else {
+		t = TimeParseAny(value)
+	}
+	return fmt.Sprintf("%d", t.Year()) + "-" + fmt.Sprintf("%02d", t.Month())
+}
+func GetDateTime(value string) string {
+	var t time.Time
+	if value == "" {
+		t = time.Now().In(TimeLoad)
+	} else {
+		t = TimeParseAny(value)
+	}
+	return fmt.Sprintf("%d", t.Year()) + "-" + fmt.Sprintf("%02d", t.Month()) + "-" + fmt.Sprintf("%02d", t.Day()) + " " + fmt.Sprintf("%02d", t.Hour()) + ":" + fmt.Sprintf("%02d", t.Minute()) + ":" + fmt.Sprintf("%02d", t.Second())
+}
+
+func TimeParseAny(value string) time.Time {
+	birStrLen := strings.Count(value, "") - 1
+	switch birStrLen {
+	case 10, 19:
+		t, _ := dateparse.ParseIn(value, TimeLoad)
+		return t
+	default:
+		t, _ := dateparse.ParseAny(value)
+		return t.In(TimeLoad)
+	}
+}
+
+func Gettime(t *time.Time) string {
+	if t == nil {
+		return ""
+	}
+	T := t.Format("2006-01-02")
+	return T
+}
+
+func TimeParse(value string) (T *time.Time) {
+	if value == "" {
+		return nil
+	}
+	birStrLen := strings.Count(value, "") - 1
+	switch birStrLen {
+	case 10, 19:
+		t, _ := dateparse.ParseIn(value, TimeLoad)
+		T = &t
+	default:
+		t, _ := dateparse.ParseAny(value)
+		t.In(TimeLoad)
+		T = &t
+	}
+	return
+}
+
+/**
+【今天】加多少天
+@param pAddDays 要加多少天?
+return 返回今天加pAddDays后的日期
+*/
+func TodayAddNDays(pAddDays int) string {
+	var t time.Time
+	t = time.Now().In(TimeLoad).AddDate(0, 0, pAddDays)
+	return fmt.Sprintf("%d", t.Year()) + "-" + fmt.Sprintf("%02d", t.Month()) + "-" + fmt.Sprintf("%02d", t.Day())
+}
+
+//开始日期
+func GetSystemBeginDate(BeginDate string) *time.Time {
+	var t *time.Time
+	if BeginDate == "" {
+		t = TimeParse(GetDateTime(BeginDate))
+	} else {
+		t = TimeParse(BeginDate)
+	}
+	return t
+}
+
+//结束日期
+func GetSystemEndDate(EndDate string) *time.Time {
+	var t *time.Time
+	if EndDate == "" {
+		t = TimeParse("2099-12-31 23:59:59")
+	} else {
+		t = TimeParse(EndDate)
+	}
+	return t
+}

+ 99 - 0
go/gopath/src/fohow.com/apps/helper/url.go

@@ -0,0 +1,99 @@
+package helper
+
+import (
+	"encoding/base64"
+	"net/url"
+	"strings"
+)
+
+type Url struct {
+	str string
+}
+
+func NewUrl(str string) *Url {
+	return &Url{str}
+}
+
+//解析 URL
+// -1: all; 1: scheme; 2: host; 4: port; 8: user; 16: pass; 32: path; 64: query; 128: fragment
+func (l *Url) ParseURL(component int) (map[string]string, error) {
+	u, err := url.Parse(l.str)
+	if err != nil {
+		return nil, err
+	}
+	if component == -1 {
+		component = 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128
+	}
+	var components = make(map[string]string)
+	if (component & 1) == 1 {
+		components["scheme"] = u.Scheme
+	}
+	if (component & 2) == 2 {
+		components["host"] = u.Hostname()
+	}
+	if (component & 4) == 4 {
+		components["port"] = u.Port()
+	}
+	if (component & 8) == 8 {
+		components["user"] = u.User.Username()
+	}
+	if (component & 16) == 16 {
+		components["pass"], _ = u.User.Password()
+	}
+	if (component & 32) == 32 {
+		components["path"] = u.Path
+	}
+	if (component & 64) == 64 {
+		components["query"] = u.RawQuery
+	}
+	if (component & 128) == 128 {
+		components["fragment"] = u.Fragment
+	}
+	return components, nil
+}
+
+//URL编码
+func (l *Url) URLEncode() string {
+	return url.QueryEscape(l.str)
+}
+
+//URL编码还原
+func (l *Url) URLDecode() (string, error) {
+	return url.QueryUnescape(l.str)
+}
+
+//按照 RFC 3986 对 URL 进行编码
+func (l *Url) Rawurlencode() string {
+	return strings.Replace(url.QueryEscape(l.str), "+", "%20", -1)
+}
+
+//对已编码的 URL 字符串进行解码
+func (l *Url) Rawurldecode() (string, error) {
+	return url.QueryUnescape(strings.Replace(l.str, "%20", "+", -1))
+}
+
+//生成 URL-encode 之后的请求字符串
+/*func (l *Url) HTTPBuildQuery(queryData url.Values) string {
+	return queryData.Encode()
+}*/
+
+//Base64编码
+func (l *Url) Base64Encode() string {
+	return base64.StdEncoding.EncodeToString([]byte(l.str))
+}
+
+//Base64编码还原
+func (l *Url) Base64Decode() (string, error) {
+	str := l.str
+	switch len(str) % 4 {
+	case 2:
+		str += "=="
+	case 3:
+		str += "="
+	}
+	data, err := base64.StdEncoding.DecodeString(str)
+	if err != nil {
+		return "", err
+	}
+	return NewRaw(data).ToStr(), nil
+}