├── src ├── plugin │ ├── email │ │ └── email.go │ ├── plugin.go │ └── wx │ │ ├── robot.go │ │ └── plugins │ │ └── plugin.go ├── app │ ├── shop.go │ ├── blog.go │ ├── manifest.go │ ├── apps.go │ ├── goods.go │ ├── pay.go │ ├── publish.go │ ├── file.go │ └── users.go ├── common │ ├── log │ │ ├── logger_test.go │ │ └── logger.go │ ├── RESULT_CODES.go │ ├── pay.go │ ├── conf │ │ ├── path.go │ │ └── conf.go │ ├── utils.go │ ├── handle.go │ ├── test_utils.go │ ├── session.go │ ├── sms.go │ └── common.go ├── bak │ └── app.toml ├── model │ ├── test │ │ ├── t_vlog_test.go │ │ ├── t_file_test.go │ │ ├── t_log_test.go │ │ ├── t_code_test.go │ │ ├── t_user_test.go │ │ └── t_app_test.go │ ├── t_vk.go │ ├── t_file.go │ ├── t_log.go │ ├── t_vlog.go │ ├── t_order.go │ ├── t_goods.go │ ├── t_app.go │ ├── t_user.go │ ├── t_publish.go │ └── t_code.go └── main.go └── Readme.md /src/plugin/email/email.go: -------------------------------------------------------------------------------- 1 | package email 2 | 3 | func Init(){ 4 | 5 | } -------------------------------------------------------------------------------- /src/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | //"./wx" 5 | "./email" 6 | ) 7 | 8 | func Init(){ 9 | // 微信插件 10 | //wx.Init() 11 | // 邮箱插件 12 | email.Init() 13 | } -------------------------------------------------------------------------------- /src/app/shop.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "net/http" 5 | "../common" 6 | "../common/log" 7 | "fmt" 8 | ) 9 | 10 | 11 | 12 | /** 13 | 添加App 14 | */ 15 | func Order(sess *common.Session, w http.ResponseWriter, r *http.Request) { 16 | r.ParseForm() 17 | //icon := r.FormValue("icon") 18 | log.N("GetTask","",fmt.Sprintf("abc%d",0)) 19 | } -------------------------------------------------------------------------------- /src/common/log/logger_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | "../conf" 7 | ".." 8 | "time" 9 | ) 10 | 11 | func TestD(t *testing.T) { 12 | common.InitDb(t) 13 | conf.Init("/home/cjwddz/桌面/git-project/golang-web/src/task.toml") 14 | fmt.Println(fmt.Sprintf("cache:%d,logInterval:%d",cache,logInterval)) 15 | Init() 16 | for i:=0;i<50;i++{ 17 | D("test","user","msg%d",i) 18 | } 19 | time.Sleep(time.Second*2) 20 | D("test","user","msg%d",123) 21 | for i:=0;i<100;i++{ 22 | D("test","user","msg%d",i) 23 | } 24 | fmt.Println("ok") 25 | } 26 | -------------------------------------------------------------------------------- /src/common/RESULT_CODES.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | 4 | const( 5 | // 操作成功 6 | CODE_SUCCESS = 200 7 | CODE_OK = 200 8 | 9 | // 请求参数错误 10 | CODE_PARAMS_INVALID=500 11 | // 系统错误 12 | CODE_SERVICE_ERR=503 13 | // 校验错误 14 | CODE_VERIFY_FAIL=404 15 | // SESSION登录超时 16 | CODE_NOT_ALLOW = 406 17 | // 需要重新登录 18 | CODE_NEET_LOGIN_AGAIN = 1024 19 | // 角色不符合 20 | CODE_ROLE_INVADE = 512 21 | // 数据库读写错误 22 | CODE_DB_RW_ERR = 128 23 | // 资源不够 24 | CODE_RESOURCE_SHORT = 601 25 | 26 | //注册-帐号已经存在 27 | REGISTER_ACCOUNT_EXIST = 1003 28 | 29 | ) 30 | 31 | const( 32 | ACTION_VIOLENCE = "潜在的攻击行为" 33 | 34 | ) 35 | -------------------------------------------------------------------------------- /src/bak/app.toml: -------------------------------------------------------------------------------- 1 | # 后台环境设置 dev-测试环境(不发送短信及邮件) normal-正式环境 2 | appEnv = "normal" 3 | # 数据库 4 | DBHost = "localhost" 5 | DBPort = "5432" 6 | DBUser = "postgres" 7 | DBPassword = "pwd" 8 | DBName = "dbName" 9 | DBNameTest = "dbName-test" 10 | DBDriver = "postgres" 11 | 12 | # 日志 13 | logCache = 100 14 | ## 最大日志间隔,单位为秒 15 | logInterval = 10 16 | # 路径 17 | staticPath = "./workdir/static" 18 | picturePath = "./workdir/static/pic" 19 | filePath = "./workdir/static/file" 20 | # 服务器 21 | serverHost="http://localhost" 22 | serverPort="80" 23 | 24 | # redis 25 | RedisHost = "localhost" 26 | RedisPort = "6379" 27 | RedisPassword = "pwd" 28 | -------------------------------------------------------------------------------- /src/common/pay.go: -------------------------------------------------------------------------------- 1 | /* 2 | 通用类 3 | */ 4 | package common 5 | 6 | import ( 7 | "crypto/md5" 8 | "fmt" 9 | "../common/conf" 10 | ) 11 | 12 | /** 13 | 获取支付链接 14 | */ 15 | func GetAliPayLink(money float32,commodity string,out_trade_no string,return_url string) (url string, err error) { 16 | notify_url:=conf.App.PayNotify 17 | pid:=conf.App.PayPid 18 | key:=conf.App.PayKey 19 | sitename:=conf.App.SiteName 20 | payType:="alipay" 21 | rawParams := fmt.Sprintf("money=%0.2f&name=%s¬ify_url=%s&out_trade_no=%s&pid=%d&return_url=%s&sitename=%s&type=%s",money,commodity,notify_url,out_trade_no,pid,return_url,sitename,payType) 22 | ciphertext := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s%s",rawParams,key)))) 23 | return fmt.Sprintf("http://pay.sddyun.cn/submit.php?%s&sign_type=MD5&sign=%s",rawParams,ciphertext),nil 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/model/test/t_vlog_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | "../../common/conf" 7 | "time" 8 | m ".." 9 | "github.com/cjwddz/fast-model" 10 | ) 11 | 12 | func TestT_vlog(t *testing.T) { 13 | conf.Init("/home/cjwddz/桌面/git-project/golang-web/src/task.toml") 14 | md, err := m.GetVLogModel() 15 | if err != nil { 16 | t.Fail() 17 | } 18 | // 正常插入 19 | vl:= m.T_vlog{ 20 | Tag:"test", 21 | Code:"test14545415", 22 | App:"testold1234", 23 | Machine:"testsgeafgweafjpagewjqogjapwikpfw", 24 | Content:"test测试日志-长", 25 | CreatedAt:time.Now(), 26 | } 27 | err = md.Insert(vl) 28 | if err != nil { 29 | fmt.Println(err.Error()) 30 | t.Fail() 31 | } 32 | cond := model.DbCondition{} 33 | rs, err := md.Query(cond.And2("=", "tag", "test").Limit2(4, -1)) 34 | if err != nil { 35 | fmt.Println("err:"+err.Error()) 36 | t.Fail() 37 | } 38 | fmt.Println(rs) 39 | fmt.Println(md.CountAll()) 40 | fmt.Println(md.Count(cond)) 41 | } -------------------------------------------------------------------------------- /src/plugin/wx/robot.go: -------------------------------------------------------------------------------- 1 | package wx 2 | 3 | import ( 4 | "github.com/songtianyi/rrframework/logs" 5 | "github.com/songtianyi/wechat-go/plugins/wxweb/faceplusplus" 6 | "github.com/songtianyi/wechat-go/plugins/wxweb/gifer" 7 | "github.com/songtianyi/wechat-go/plugins/wxweb/replier" 8 | "github.com/songtianyi/wechat-go/plugins/wxweb/switcher" 9 | "github.com/songtianyi/wechat-go/wxweb" 10 | ) 11 | 12 | func Init() { 13 | // 创建session, 一个session对应一个机器人 14 | // 二维码显示在终端上 15 | session, err := wxweb.CreateSession(nil, nil, wxweb.WEB_MODE) 16 | if err != nil { 17 | logs.Error(err) 18 | return 19 | } 20 | 21 | // 注册插件, 所有插件默认是开启的 22 | faceplusplus.Register(session) 23 | replier.Register(session) 24 | switcher.Register(session) 25 | gifer.Register(session) 26 | 27 | // 你也可以自己选择关闭插件里的handler(消息处理器) 28 | session.HandlerRegister.DisableByName("faceplusplus") 29 | 30 | // 登录并接收消息 31 | if err := session.LoginAndServe(false); err != nil { 32 | logs.Error("session exit, %s", err) 33 | } 34 | } -------------------------------------------------------------------------------- /src/common/conf/path.go: -------------------------------------------------------------------------------- 1 | /** 2 | * 一些读取配置用到的公共函数 3 | */ 4 | package conf 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // 项目根目录 13 | var AppRootPath string 14 | 15 | // AppPath 项目根目录 16 | func AppPath(appPath ...string) string { 17 | if len(appPath) > 0 { 18 | AppRootPath = appPath[0] 19 | } 20 | if AppRootPath == "" { 21 | AppRootPath, _ = filepath.Abs(filepath.Dir(os.Args[0])) 22 | if !fileExists(filepath.Join(AppRootPath, "conf", "task.toml")) { 23 | workPath, _ := os.Getwd() 24 | workPath, _ = filepath.Abs(workPath) 25 | AppRootPath = workPath 26 | } 27 | } 28 | return AppRootPath 29 | } 30 | 31 | // RealFilePath 返回绝对路径 32 | func RealFilePath(relFilename string) string { 33 | if strings.HasPrefix(relFilename, "/") || relFilename[1]==':'{ 34 | return relFilename 35 | } 36 | return filepath.Join(AppPath(), relFilename) 37 | } 38 | 39 | func fileExists(name string) bool { 40 | if _, err := os.Stat(name); err != nil { 41 | if os.IsNotExist(err) { 42 | return false 43 | } 44 | } 45 | return true 46 | } 47 | -------------------------------------------------------------------------------- /src/model/test/t_file_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | "../../common/conf" 7 | "time" 8 | m ".." 9 | "github.com/cjwddz/fast-model" 10 | ) 11 | 12 | func TestT_file(t *testing.T) { 13 | conf.Init("/home/cjwddz/桌面/git-project/golang-web/src/task.toml") 14 | md, err := m.GetFileModel() 15 | if err != nil { 16 | t.Fail() 17 | } 18 | // 正常插入 19 | file := m.T_File{ 20 | Key:"test fileKey", 21 | Type:"test fileType", 22 | Name:"test fileName", 23 | Owner:-1, 24 | CreatedAt:time.Now(), 25 | } 26 | err = md.Insert(file) 27 | if err != nil { 28 | fmt.Println(err.Error()) 29 | t.Fail() 30 | } 31 | cond := model.DbCondition{} 32 | rs, err := md.Query(cond.And2(">", "id", 0).Limit2(4, -1)) 33 | if err != nil { 34 | fmt.Println(err.Error()) 35 | t.Fail() 36 | } 37 | sq:=model.DbSetCondition{} 38 | err = md.Update(sq.And2("!=", "name", "test fileName").Set2("name","test fileNameUpdated")) 39 | if err != nil { 40 | fmt.Println(err.Error()) 41 | t.Fail() 42 | } 43 | fmt.Println(rs) 44 | fmt.Println(md.CountAll()) 45 | fmt.Println(md.Count(cond)) 46 | } -------------------------------------------------------------------------------- /src/model/test/t_log_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | "../../common/conf" 7 | "time" 8 | m ".." 9 | "github.com/cjwddz/fast-model" 10 | ) 11 | 12 | func TestT_log(t *testing.T) { 13 | conf.Init("/home/cjwddz/桌面/git-project/golang-web/src/task.toml") 14 | md, err := m.GetLogModel() 15 | if err != nil { 16 | t.Fail() 17 | } 18 | // 正常插入 19 | item:=m.T_log{ 20 | Type: "debug", 21 | Tag: "test 测试用例", 22 | Operator: "tester", 23 | Content: "test 忽略我吧,我是测试用例!", 24 | CreatedAt: time.Now(), 25 | } 26 | err = md.Insert(item) 27 | if err != nil { 28 | fmt.Println(err.Error()) 29 | t.Fail() 30 | } 31 | cond := model.DbCondition{} 32 | rs, err := md.Query(cond.And2("=", "tag", "test 测试用例").Limit2(4, -1)) 33 | if err != nil { 34 | fmt.Println(err.Error()) 35 | t.Fail() 36 | } 37 | sq:=model.DbSetCondition{} 38 | err = md.Update(sq.And2("=", "tag", "test 测试用例").Set2("tag","test update")) 39 | if err != nil { 40 | fmt.Println(err.Error()) 41 | t.Fail() 42 | } 43 | fmt.Println(rs) 44 | fmt.Println(md.CountAll()) 45 | fmt.Println(md.Count(cond)) 46 | } -------------------------------------------------------------------------------- /src/common/utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | "strconv" 7 | "bytes" 8 | "encoding/base64" 9 | ) 10 | 11 | /** 12 | 获取随机字符串 13 | */ 14 | func GetRandomString(l int) string { 15 | str := "0123456789ASDFGHJKLPOIUYTREWQZXCVBNM" 16 | bytes := []byte(str) 17 | result := []byte{} 18 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 19 | for i := 0; i < l; i++ { 20 | result = append(result, bytes[r.Intn(len(bytes))]) 21 | } 22 | return string(result) 23 | } 24 | /** 25 | 获取随机数字 26 | */ 27 | func GetRandomInt(l int) string { 28 | str := "0123456789" 29 | bytes := []byte(str) 30 | result := []byte{} 31 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 32 | result = append(result, bytes[r.Intn(len(bytes)-1)+1]) 33 | for i := 1; i < l; i++ { 34 | result = append(result, bytes[r.Intn(len(bytes))]) 35 | } 36 | return string(result) 37 | } 38 | 39 | /** 40 | 字符串转ints 41 | */ 42 | func BytesToInt(data []byte) string { 43 | buffer := new(bytes.Buffer) 44 | for _, b := range data { 45 | s := strconv.FormatInt(int64(b&0xff), 10) 46 | buffer.WriteString(s) 47 | } 48 | return buffer.String() 49 | } 50 | 51 | /** 52 | 转base64 53 | */ 54 | func ToBase64(data []byte) string{ 55 | return base64.StdEncoding.EncodeToString(data) 56 | } -------------------------------------------------------------------------------- /src/model/test/t_code_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | "../../common/conf" 7 | "github.com/bitly/go-simplejson" 8 | "time" 9 | m ".." 10 | "github.com/cjwddz/fast-model" 11 | ) 12 | 13 | func TestT_code(t *testing.T) { 14 | conf.Init("/home/cjwddz/桌面/git-project/golang-web/src/task.toml") 15 | md, err := m.GetCodeModel() 16 | if err != nil { 17 | t.Fail() 18 | } 19 | // 正常插入 20 | code:=m.T_code{ 21 | Code:"testCode....", 22 | AppId:"sgwj2000jojo", 23 | Developer:125, 24 | Consumer:simplejson.New(), 25 | Describe:"测试", 26 | Valid:true, 27 | MachineCount:5, 28 | EnableTime:false, 29 | StartTime:time.Now(), 30 | EndTime:time.Now(), 31 | CreatedAt:time.Now(), 32 | } 33 | err = md.Insert(code) 34 | if err != nil { 35 | fmt.Println(err.Error()) 36 | t.Fail() 37 | } 38 | cond := model.DbCondition{} 39 | rs, err := md.Query(cond.And2("=", "valid", true).Limit2(4, -1)) 40 | if err != nil { 41 | fmt.Println(err.Error()) 42 | t.Fail() 43 | } 44 | sq:=model.DbSetCondition{} 45 | err = md.Update(sq.And2("=", "app_id", "sgwj2000jojo").Set2("app_id","resetAppId")) 46 | if err != nil { 47 | fmt.Println(err.Error()) 48 | t.Fail() 49 | } 50 | fmt.Println(rs) 51 | fmt.Println(md.CountAll()) 52 | fmt.Println(md.Count(cond)) 53 | } -------------------------------------------------------------------------------- /src/model/t_vk.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | m "github.com/cjwddz/fast-model" 6 | ) 7 | 8 | /* 9 | DROP TABLE IF EXISTS t_file; 10 | CREATE TABLE t_vk ( 11 | "key" varchar(16) UNIQUE COLLATE "default", 12 | "value" text, 13 | "update_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP, 14 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 15 | ) 16 | WITH (OIDS=FALSE); 17 | */ 18 | type T_Vk struct { 19 | Key string `json:"key"` 20 | Value string `json:"value"` 21 | Owner string `json:"owner,omitempty"` 22 | UpdateAt time.Time `json:"update_at"` 23 | CreatedAt time.Time `json:"created_at"` 24 | } 25 | 26 | func GetVkModel() (m.DbModel, error){ 27 | sc:=m.SqlController { 28 | TableName: "t_vk", 29 | InsertColumns: []string{"key","value"}, 30 | QueryColumns: []string{"key","value","update_at","created_at"}, 31 | InSertFields: insertVkFields, 32 | QueryField2Obj: queryVkField2Obj, 33 | } 34 | return m.GetModel(sc) 35 | } 36 | 37 | func insertVkFields(obj interface{}) []interface{} { 38 | vk:=obj.(T_Vk) 39 | return []interface{}{ 40 | vk.Key, vk.Value, 41 | } 42 | } 43 | func queryVkField2Obj(fields []interface{}) interface{} { 44 | file := T_Vk{ 45 | Key: m.GetString(fields[1]), 46 | Value: m.GetString(fields[2]), 47 | UpdateAt: m.GetTime(fields[9], time.Now()), 48 | CreatedAt: m.GetTime(fields[9], time.Now()), 49 | } 50 | return file 51 | } -------------------------------------------------------------------------------- /src/model/test/t_user_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | "../../common/conf" 7 | "github.com/bitly/go-simplejson" 8 | "time" 9 | m ".." 10 | "github.com/cjwddz/fast-model" 11 | ) 12 | 13 | func TestT_user(t *testing.T) { 14 | conf.Init("/home/cjwddz/桌面/git-project/golang-web/src/task.toml") 15 | md, err := m.GetUserModel() 16 | if err != nil { 17 | t.Fail() 18 | } 19 | // 正常插入 20 | v:=simplejson.New() 21 | v.Set("test","avalue") 22 | user:=m.T_user{ 23 | Role:m.USER_ROLE_ADMIN, 24 | Nick:"test nick", 25 | Account:"test account", 26 | Pwd:"test xxxxx", 27 | Status:2, 28 | Avatar:"test https://avatars2.githubusercontent.com/u/24471738?v=4&s=40", 29 | Phone:"13480332034", 30 | Email:"cjwddz@qq.com", 31 | QQ:"1436983000", 32 | Expend: v, 33 | UpdatedAt:time.Now(), 34 | CreatedAt:time.Now(), 35 | } 36 | err = md.Insert(user) 37 | if err != nil { 38 | fmt.Println(err.Error()) 39 | t.Fail() 40 | } 41 | cond := model.DbCondition{} 42 | rs, err := md.Query(cond.And2("=", "nick", "test nick").Limit2(4, -1)) 43 | if err != nil { 44 | fmt.Println(err.Error()) 45 | t.Fail() 46 | } 47 | sq:=model.DbSetCondition{} 48 | err = md.Update(sq.And2("=", "nick", "test nick").Set2("nick","test update nickkk")) 49 | if err != nil { 50 | fmt.Println(err.Error()) 51 | t.Fail() 52 | } 53 | fmt.Println(rs) 54 | fmt.Println(md.CountAll()) 55 | fmt.Println(md.Count(cond)) 56 | } -------------------------------------------------------------------------------- /src/common/handle.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | /** 8 | 初始化路由 9 | */ 10 | func SetRouters(routers []BH){ 11 | for i:=0; i< len(routers) ; i++{ 12 | http.HandleFunc(routers[i].Url, routers[i].BsHandle) 13 | } 14 | } 15 | 16 | /** 17 | 处理基类 18 | Url: 路由 19 | Check: 校验用户合法性 20 | Handle: 处理函数 21 | */ 22 | type BH struct{ 23 | Url string 24 | Check bool 25 | Handle func(*Session,http.ResponseWriter, *http.Request) 26 | } 27 | /** 28 | 处理 29 | */ 30 | func (hd *BH) BsHandle(w http.ResponseWriter, r *http.Request){ 31 | hd.SetContent(w,r) 32 | if !hd.Check{ 33 | // 不需要校验Session 34 | hd.Handle(nil,w,r) 35 | return 36 | } 37 | if err:=CheckSession(w,r);err!=nil{ 38 | ReturnEFormat(w, CODE_NEET_LOGIN_AGAIN, err.Error()) 39 | return 40 | } 41 | // 需要校验Session 42 | sessionId:="" 43 | if cookie0,err0 := r.Cookie("sessionId");err0==nil{ 44 | sessionId= cookie0.Value 45 | }else if sessionId = r.FormValue("sessionId");sessionId==""{ 46 | sessionId = r.PostFormValue("sessionId") 47 | } 48 | session,_:=UserSessions.Load(sessionId) 49 | hd.Handle(session.(*Session),w,r) 50 | } 51 | 52 | /** 53 | 设置跨域访问 54 | */ 55 | func (hd *BH) SetContent(w http.ResponseWriter, r *http.Request) { 56 | origin:=r.Header.Get("Origin") 57 | w.Header().Set("Access-Control-Allow-Origin", origin) //允许访问所有域 58 | w.Header().Set("Access-Control-Allow-Credentials","true") 59 | w.Header().Add("Access-Control-Allow-Headers", "x-requested-with,content-type") //header的类型 60 | } -------------------------------------------------------------------------------- /src/app/blog.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import "os" 4 | 5 | import ( 6 | "../common" 7 | "../common/log" 8 | "net/http" 9 | "io" 10 | "../common/conf" 11 | "../model" 12 | "time" 13 | ) 14 | 15 | /** 16 | 上传小文件 17 | */ 18 | func UploadMarkdown(sess *common.Session, w http.ResponseWriter, r *http.Request) { 19 | // todo 20 | f, h, err := r.FormFile("markdown") 21 | if err != nil { 22 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID,"请提交文件") 23 | return 24 | } 25 | defer f.Close() 26 | fileKey := r.FormValue("sha256") 27 | if len(fileKey)!=64{ 28 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID,"文件指纹缺失!") 29 | return 30 | } 31 | fileType:=r.FormValue("type") 32 | if fileType==""{ 33 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID,"未指定文件类型!") 34 | return 35 | } 36 | filePath := conf.App.PathFile +"/"+ fileKey 37 | t, err := os.Create(filePath) 38 | if err != nil { 39 | common.ReturnEFormat(w, common.CODE_SERVICE_ERR, "内部服务出错!") 40 | log.E("UploadFile出错",sess.User.Email,err.Error()) 41 | return 42 | } 43 | defer t.Close() 44 | if _, err := io.Copy(t, f); err != nil { 45 | common.ReturnEFormat(w, common.CODE_SERVICE_ERR, "内部服务出错!") 46 | log.E("UploadFile出错",sess.User.Email,err.Error()) 47 | return 48 | } 49 | file:=model.T_File{ 50 | Key:fileKey, 51 | Type:fileType, 52 | Name:h.Filename, 53 | Owner:sess.User.Email, 54 | CreatedAt:time.Now(), 55 | } 56 | err=FileModel.Insert(file) 57 | if err!=nil{ 58 | log.E("UploadFile出错",sess.User.Email,err.Error()) 59 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "数据库写入失败!") 60 | return 61 | } 62 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"key": fileKey,"msg":"success"}) 63 | log.N("UploadFile上传文件成功",sess.User.Email,h.Filename) 64 | } 65 | -------------------------------------------------------------------------------- /src/plugin/wx/plugins/plugin.go: -------------------------------------------------------------------------------- 1 | package wxPlugin // 以插件名命令包名 2 | 3 | import ( 4 | "github.com/songtianyi/rrframework/logs" // 导入日志包 5 | "github.com/songtianyi/wechat-go/wxweb" // 导入协议包 6 | ) 7 | 8 | // 必须有的插件注册函数 9 | // 指定session, 可以对不同用户注册不同插件 10 | func Register(session *wxweb.Session) { 11 | // 将插件注册到session 12 | // 第一个参数: 指定消息类型, 所有该类型的消息都会被转发到此插件 13 | // 第二个参数: 指定消息处理函数, 消息会进入此函数 14 | // 第三个参数: 自定义插件名,不能重名,switcher插件会用到此名称 15 | session.HandlerRegister.Add(wxweb.MSG_TEXT, wxweb.Handler(demo), "textdemo") 16 | 17 | // 开启插件 18 | if err := session.HandlerRegister.EnableByName("textdemo"); err != nil { 19 | logs.Error(err) 20 | } 21 | } 22 | 23 | // 消息处理函数 24 | func demo(session *wxweb.Session, msg *wxweb.ReceivedMessage) { 25 | 26 | // 可选: 可以用contact manager来过滤, 比如过滤掉没有保存到通讯录的群 27 | // 注意,contact manager只存储了已保存到通讯录的群组 28 | contact := session.Cm.GetContactByUserName(msg.FromUserName) 29 | if contact == nil { 30 | logs.Error("ignore the messages from", msg.FromUserName) 31 | return 32 | } 33 | 34 | // 可选: 根据消息类型来过滤 35 | if msg.MsgType == wxweb.MSG_IMG { 36 | return 37 | } 38 | 39 | // 可选: 根据wxweb.User数据结构中的数据来过滤 40 | if contact.PYQuanPin != "songtianyi" { 41 | // 比如根据用户昵称的拼音全拼来过滤 42 | return 43 | } 44 | 45 | // 可选: 过滤和自己无关的群组消息 46 | if msg.IsGroup && msg.Who != session.Bot.UserName { 47 | return 48 | } 49 | 50 | // 取出收到的内容 51 | // 取text 52 | logs.Info(msg.Content) 53 | //// 取img 54 | //if b, err := session.GetImg(msg.MsgId); err == nil { 55 | // logs.Debug(string(b)) 56 | //} 57 | 58 | // anything 59 | 60 | // 回复消息 61 | // 第一个参数: 回复的内容 62 | // 第二个参数: 机器人ID 63 | // 第三个参数: 联系人/群组/特殊账号ID 64 | session.SendText("plugin demo", session.Bot.UserName, wxweb.RealTargetUserName(session, msg)) 65 | // 回复图片和gif 参见wxweb/session.go 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/app/manifest.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/cjwddz/fast-model" 5 | "fmt" 6 | m "../model" 7 | "../common/conf" 8 | ) 9 | var AppModel model.DbModel 10 | var CodeModel model.DbModel 11 | var UserModel model.DbModel 12 | var VlogModel model.DbModel 13 | var FileModel model.DbModel 14 | var PublishModel model.DbModel 15 | var GoodModel model.DbModel 16 | var OrderModel model.DbModel 17 | 18 | 19 | func Init(){ 20 | if !model.DbHasInit{ 21 | if conf.App.AppEvn=="dev"{ 22 | model.InitDB(conf.App.DBHost, conf.App.DBPort, conf.App.DBUser, conf.App.DBPassword, conf.App.TestDbName, conf.App.DBDriver) 23 | }else{ 24 | model.InitDB(conf.App.DBHost, conf.App.DBPort, conf.App.DBUser, conf.App.DBPassword, conf.App.DBName, conf.App.DBDriver) 25 | } 26 | } 27 | app,err:= m.GetAppModel() 28 | if err!=nil{ 29 | fmt.Println(err.Error()) 30 | return 31 | } 32 | code,err:=m.GetCodeModel() 33 | if err!=nil{ 34 | fmt.Println(err.Error()) 35 | return 36 | } 37 | user,err:=m.GetUserModel() 38 | if err!=nil{ 39 | fmt.Println(err.Error()) 40 | return 41 | } 42 | file,err:=m.GetFileModel() 43 | if err!=nil{ 44 | fmt.Println(err.Error()) 45 | return 46 | } 47 | vlog,err:=m.GetVLogModel() 48 | if err!=nil{ 49 | fmt.Println(err.Error()) 50 | return 51 | } 52 | publish,err:=m.GetPublishModel() 53 | if err!=nil{ 54 | fmt.Println(err.Error()) 55 | return 56 | } 57 | good,err:=m.GetGoodModel() 58 | if err!=nil{ 59 | fmt.Println(err.Error()) 60 | return 61 | } 62 | order,err:=m.GetOrderModel() 63 | if err!=nil{ 64 | fmt.Println(err.Error()) 65 | return 66 | } 67 | AppModel = app 68 | CodeModel = code 69 | UserModel = user 70 | FileModel = file 71 | VlogModel = vlog 72 | PublishModel = publish 73 | GoodModel = good 74 | OrderModel = order 75 | } 76 | -------------------------------------------------------------------------------- /src/model/t_file.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | m "github.com/cjwddz/fast-model" 6 | ) 7 | 8 | /* 9 | DROP TABLE IF EXISTS t_file; 10 | CREATE TABLE t_file ( 11 | "id" serial NOT NULL, 12 | "key" varchar(16) UNIQUE COLLATE "default", 13 | "type" varchar(16) COLLATE "default", 14 | "size" int, 15 | "name" varchar(128) COLLATE "default", 16 | "owner" varchar(128) COLLATE "default", 17 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 18 | ) 19 | WITH (OIDS=FALSE); 20 | */ 21 | type T_File struct { 22 | ID int64 `json:"id"` 23 | Key string `json:"key"` 24 | Type string `json:"type"` 25 | Size int64 `json:"size"` 26 | Name string `json:"name"` 27 | Owner string `json:"owner,omitempty"` 28 | CreatedAt time.Time `json:"created_at"` 29 | } 30 | 31 | func GetFileModel() (m.DbModel, error){ 32 | sc:=m.SqlController { 33 | TableName: "t_file", 34 | InsertColumns: []string{"key","type","size","name","owner","created_at"}, 35 | QueryColumns: []string{"id","key","type","size","name","owner","created_at"}, 36 | InSertFields: insertFileFields, 37 | QueryField2Obj: queryFileField2Obj, 38 | } 39 | return m.GetModel(sc) 40 | } 41 | 42 | func insertFileFields(obj interface{}) []interface{} { 43 | file:=obj.(T_File) 44 | return []interface{}{ 45 | file.Key, file.Type, file.Size, file.Name, file.Owner, file.CreatedAt, 46 | } 47 | } 48 | func queryFileField2Obj(fields []interface{}) interface{} { 49 | file:=T_File{ 50 | ID: m.GetInt64(fields[0],0), 51 | Key: m.GetString(fields[1]), 52 | Type: m.GetString(fields[2]), 53 | Size: m.GetInt64(fields[3],0), 54 | Name: m.GetString(fields[4]), 55 | Owner: m.GetString(fields[5]), 56 | CreatedAt: m.GetTime(fields[6],time.Now()), 57 | } 58 | return file 59 | } -------------------------------------------------------------------------------- /src/common/test_utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net/url" 5 | "strings" 6 | "net/http/httptest" 7 | "fmt" 8 | "net/http" 9 | "errors" 10 | "../model" 11 | "time" 12 | "sync" 13 | ) 14 | /** 15 | Http模拟(无验证) 16 | */ 17 | func MockHttp(v url.Values, route string,handle func(http.ResponseWriter,*http.Request))(res string, err error){ 18 | req, err := http.NewRequest("POST", route, strings.NewReader(v.Encode())) 19 | if err != nil { 20 | return 21 | } 22 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 23 | rr := httptest.NewRecorder() 24 | handler := http.HandlerFunc(handle) 25 | handler.ServeHTTP(rr, req) 26 | if status := rr.Code; status != http.StatusOK { 27 | err = errors.New(fmt.Sprintf("status code: %d",status)) 28 | } 29 | res = rr.Body.String() 30 | return 31 | } 32 | /** 33 | Http模拟(过验证) 34 | */ 35 | func MockHttp2(v url.Values, route string,user model.T_user, handle func(http.ResponseWriter,*http.Request))(res string, err error){ 36 | UserSessions=sync.Map{} 37 | UserSessions.Store("TEST_SESSION",&Session{ 38 | User: user, 39 | Token:"TEST_TOKEN", 40 | Time:time.Now(), 41 | }) 42 | req, err := http.NewRequest("POST", route, strings.NewReader(v.Encode())) 43 | if err != nil { 44 | return 45 | } 46 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 47 | req.AddCookie(&http.Cookie{ 48 | Name:"sessionId", 49 | Value:"TEST_SESSION", 50 | }) 51 | req.AddCookie(&http.Cookie{ 52 | Name:"token", 53 | Value:"TEST_TOKEN", 54 | }) 55 | rr := httptest.NewRecorder() 56 | handler := http.HandlerFunc(handle) 57 | handler.ServeHTTP(rr, req) 58 | if status := rr.Code; status != http.StatusOK { 59 | err = errors.New(fmt.Sprintf("status code: %d",status)) 60 | } 61 | res = rr.Body.String() 62 | return 63 | } -------------------------------------------------------------------------------- /src/model/t_log.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | m "github.com/cjwddz/fast-model" 6 | ) 7 | /* 8 | DROP TABLE IF EXISTS t_log; 9 | CREATE TABLE t_log ( 10 | "id" serial NOT NULL, 11 | "type" varchar(16) COLLATE "default", 12 | "tag" varchar(256) COLLATE "default", 13 | "operator" varchar(128) COLLATE "default", 14 | "content" varchar(512) COLLATE "default", 15 | "caller" varchar(512) COLLATE "default", 16 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 17 | ) 18 | WITH (OIDS=FALSE); 19 | */ 20 | 21 | const( 22 | LOG_TYPE_DEBUG="debug" 23 | LOG_TYPE_INFO="info" 24 | LOG_TYPE_WARM="warn" 25 | LOG_TYPE_ERROR="error" 26 | LOG_TYPE_NORMAL="normal" 27 | LOG_TYPE_NOTIFY="notify" 28 | ) 29 | 30 | type T_log struct { 31 | ID int64 `json:"id"` 32 | Type string `json:"type"` 33 | Tag string `json:"tag"` 34 | Operator string `json:"operator"` 35 | Content string `json:"content"` 36 | Caller string `json:"caller"` 37 | CreatedAt time.Time `json:"created_at"` 38 | } 39 | 40 | func GetLogModel() (m.DbModel, error){ 41 | sc:=m.SqlController { 42 | TableName: "t_log", 43 | InsertColumns: []string{"type","tag","operator","content","caller","created_at"}, 44 | QueryColumns: []string{"id","type","tag","operator","content","caller","created_at"}, 45 | InSertFields: insertLogFields, 46 | QueryField2Obj: queryLogField2Obj, 47 | } 48 | return m.GetModel(sc) 49 | } 50 | func insertLogFields(obj interface{}) []interface{} { 51 | log :=obj.(T_log) 52 | return []interface{}{ 53 | log.Type, log.Tag, log.Operator, log.Content,log.Caller, log.CreatedAt, 54 | } 55 | } 56 | func queryLogField2Obj(fields []interface{}) interface{} { 57 | log:=T_log{ 58 | ID:m.GetInt64(fields[0],0), 59 | Type:m.GetString(fields[1]), 60 | Tag:m.GetString(fields[2]), 61 | Operator:m.GetString(fields[3]), 62 | Content:m.GetString(fields[4]), 63 | Caller:m.GetString(fields[5]), 64 | CreatedAt:m.GetTime(fields[6],time.Now()), 65 | } 66 | return log 67 | } -------------------------------------------------------------------------------- /src/common/conf/conf.go: -------------------------------------------------------------------------------- 1 | /** 2 | 配置文件 3 | */ 4 | package conf 5 | 6 | import ( 7 | "github.com/BurntSushi/toml" 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | type tomlFile struct { 13 | // site 14 | SiteName string `toml:"siteName"` 15 | AppEvn string `toml:"appEnv"` 16 | // 数据库 17 | DBHost string `toml:"dBHost"` 18 | DBPort string `toml:"dBPort"` 19 | DBUser string `toml:"dBUser"` 20 | DBPassword string `toml:"dBPassword"` 21 | DBName string `toml:"dBName"` 22 | TestDbName string `toml:"dBNameTest"` 23 | DBDriver string `toml:"dBDriver"` 24 | // 路径 25 | PathFile string `toml:"filePath"` 26 | StaticPath string `toml:"staticPath"` 27 | // 服务器 28 | ServerHost string `toml:"serverHost"` 29 | ServerPort string `toml:"serverPort"` 30 | // 日志 31 | LogCache int `toml:"logCache"` 32 | LogInterval int `toml:"logInterval"` 33 | LogWriteDb bool `toml:"logWriteDb"` 34 | LogOutConsole bool `toml:"logOutConsole"` 35 | // 支付系统 36 | PayPid int `toml:"payPid"` 37 | PayKey string `toml:"payKey"` 38 | PayNotify string `toml:"payNotify"` 39 | // redis 40 | RedisHost string `toml:"redisHost"` 41 | RedisPort string `toml:"redisPort"` 42 | RedisPassword string `toml:"redisPassword"` 43 | } 44 | 45 | var App *tomlFile 46 | 47 | func init() { 48 | App = new(tomlFile) 49 | } 50 | 51 | func Init(filePath string) { 52 | var path=RealFilePath(filePath) 53 | fmt.Println(fmt.Sprintf("\x1b[%dm加载配置文件:%s\x1b[0m",uint8(93),path)) 54 | _, err := toml.DecodeFile(path, App) 55 | if err != nil { 56 | fmt.Println(fmt.Sprintf("\x1b[%dm文件不存在,请检查指定路径是否存在配置文件。!detail:%s\x1b[0m",uint8(91),err.Error())) 57 | panic(err) 58 | } 59 | fmt.Println(fmt.Sprintf("\x1b[%dm配置:%s\x1b[0m",uint8(93),App)) 60 | checkDirs() 61 | } 62 | /** 63 | 检查文件目录 64 | */ 65 | func checkDirs(){ 66 | for _,path:=range []string{App.StaticPath,App.PathFile} { 67 | rp:=RealFilePath(path) 68 | if _,err:=os.Stat(rp);err!=nil{ 69 | fmt.Println(fmt.Sprintf("\x1b[%dm路径不存在%s,即将重新创建。detail:%s\x1b[0m",uint8(91),rp,err.Error())) 70 | err:=os.MkdirAll(rp,0755) 71 | if err!=nil{ 72 | fmt.Println(fmt.Sprintf("\x1b[%dm创建路径失败:%s。detail:%s\x1b[0m",uint8(91),rp,err.Error())) 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/model/t_vlog.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | m "github.com/cjwddz/fast-model" 6 | ) 7 | 8 | /* 9 | DROP TABLE IF EXISTS t_vlog; 10 | CREATE TABLE t_vlog ( 11 | "id" serial NOT NULL, 12 | "tag" varchar(255) COLLATE "default", 13 | "code" varchar(16) COLLATE "default", 14 | "app_id" varchar(16) COLLATE "default", 15 | "machine" varchar(128) COLLATE "default", 16 | "content" varchar(255) COLLATE "default", 17 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 18 | ) 19 | WITH (OIDS=FALSE); 20 | */ 21 | const ( 22 | VERIFY_LOG_SUCCESS = "验证成功" 23 | 24 | VERIFY_LOG_NOEXIST = "邀请码不存在" 25 | VERIFY_LOG_INVALID = "邀请码已经禁用" 26 | VERIFY_LOG_NOFIND_APP = "找不到指定app" 27 | VERIFY_LOG_APP_NOVALID = "该APP已失效" 28 | VERIFY_LOG_EMPTY = "邀请码为空" 29 | VERIFY_LOG_MACHINE_NOVALID = "机器码不可用" 30 | VERIFY_LOG_TOOMUCH_MACHINE = "设备数超限制" 31 | VERIFY_LOG_BEFORE_TIME = "尚未生效" 32 | VERIFY_LOG_AFTER_TIME = "已经过期" 33 | 34 | VERIFY_LOG_PROTO_NOVALID = "协议码不可用" 35 | ) 36 | 37 | type T_vlog struct { 38 | ID int64 `json:"id"` 39 | Tag string `json:"tag"` 40 | Code string `json:"code"` 41 | App string `json:"app_id"` 42 | Machine string `json:"machine"` 43 | Content string `json:"content"` 44 | CreatedAt time.Time `json:"created_at"` 45 | } 46 | 47 | func GetVLogModel() (m.DbModel, error){ 48 | sc:=m.SqlController { 49 | TableName: "t_vlog", 50 | InsertColumns: []string{"tag","code","app_id","machine","content","created_at"}, 51 | QueryColumns: []string{"id","tag","code","app_id","machine","content","created_at"}, 52 | InSertFields: insertVLogFields, 53 | QueryField2Obj: queryVLogField2Obj, 54 | } 55 | return m.GetModel(sc) 56 | } 57 | 58 | func insertVLogFields(obj interface{}) []interface{} { 59 | vlog:=obj.(T_vlog) 60 | return []interface{}{ 61 | vlog.Tag,vlog.Code,vlog.App,vlog.Machine,vlog.Content,vlog.CreatedAt, 62 | } 63 | } 64 | func queryVLogField2Obj(fields []interface{}) interface{} { 65 | vl:=T_vlog{ 66 | ID:m.GetInt64(fields[0],0), 67 | Tag:m.GetString(fields[1]), 68 | Code:m.GetString(fields[2]), 69 | App:m.GetString(fields[3]), 70 | Machine:m.GetString(fields[4]), 71 | Content:m.GetString(fields[5]), 72 | CreatedAt:m.GetTime(fields[6],time.Now()), 73 | } 74 | return vl 75 | } -------------------------------------------------------------------------------- /src/app/apps.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "net/http" 5 | "../common" 6 | fm "github.com/cjwddz/fast-model" 7 | "../model" 8 | "fmt" 9 | "time" 10 | "github.com/bitly/go-simplejson" 11 | ) 12 | 13 | /** 14 | 添加App 15 | */ 16 | func AddApp(sess *common.Session, w http.ResponseWriter, r *http.Request) { 17 | r.ParseForm() 18 | icon := r.FormValue("icon") 19 | version := r.FormValue("version") 20 | describe := r.FormValue("describe") 21 | name:=r.FormValue("name") 22 | file:=r.FormValue("file") 23 | src:=r.FormValue("src") 24 | _valid :=r.FormValue("valid") 25 | valid:=true 26 | if _valid =="" || _valid =="false"{ 27 | valid=false 28 | } 29 | if name == ""{ 30 | common.ReturnEFormat(w, common.CODE_VERIFY_FAIL, "请输入App名称") 31 | return 32 | } 33 | if version==""{ 34 | common.ReturnEFormat(w, common.CODE_VERIFY_FAIL, "版本号不能为空") 35 | return 36 | } 37 | if describe==""{ 38 | common.ReturnEFormat(w, common.CODE_VERIFY_FAIL, "App描述不能为空") 39 | return 40 | } 41 | if file==""{ 42 | common.ReturnEFormat(w, common.CODE_VERIFY_FAIL, "请先上传App") 43 | return 44 | } 45 | if src!=""{ 46 | // todo 检测文件有效性 47 | } 48 | app:=&model.T_app{ 49 | Icon:icon, 50 | AppId:common.GetRandomString(16), 51 | Name:name, 52 | Version:version, 53 | Describe:describe, 54 | Valid:valid, 55 | File:file, 56 | Src:src, 57 | Expend:simplejson.New(), // TODO 58 | DownloadCount:0, 59 | CreatedAt:time.Now(), 60 | } 61 | err:=AppModel.Insert(app) 62 | if err!=nil{ 63 | common.ReturnEFormat(w,common.CODE_DB_RW_ERR, "数据库插入错误") 64 | return 65 | } 66 | common.ReturnFormat(w,common.CODE_OK,map[string]interface{}{"msg":"操作成功!"}) 67 | } 68 | 69 | /** 70 | 获取App 71 | */ 72 | func ListApps(sess *common.Session,w http.ResponseWriter, r *http.Request){ 73 | if sess.User.Role!=model.USER_ROLE_DEVELOPER && sess.User.Role!=model.USER_ROLE_SUPER && sess.User.Role!=model.USER_ROLE_ADMIN{ 74 | common.ReturnEFormat(w, common.CODE_ROLE_INVADE,"你没有获取APP列表的权限!") 75 | return 76 | } 77 | r.ParseForm() 78 | cdt:=fm.DbCondition{} 79 | if sess.User.Role==model.USER_ROLE_DEVELOPER{ 80 | cdt=cdt.And("=","developer",sess.User.Id) 81 | } 82 | cdt=cdt.And2(r,"=","b_valid").And2(r,"like","s_name") 83 | cdt=cdt.Order(r.FormValue("order")).Limit2(r,"start","len") 84 | apps,err:= AppModel.Query(cdt) 85 | if err!=nil{ 86 | fmt.Println(err.Error()) 87 | } 88 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"apps":apps,"msg":"操作成功!"}) 89 | } 90 | 91 | /** 92 | 删除App 93 | */ 94 | func DeleteApps(w http.ResponseWriter, r *http.Request, user *model.T_user){ 95 | 96 | } -------------------------------------------------------------------------------- /src/model/t_order.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | m "github.com/cjwddz/fast-model" 6 | "github.com/bitly/go-simplejson" 7 | ) 8 | 9 | /* 10 | DROP TABLE IF EXISTS t_orders; 11 | CREATE TABLE t_orders ( 12 | "id" serial NOT NULL, 13 | "type" varchar(16) COLLATE "default", 14 | "channel" varchar(16) COLLATE "default", 15 | "order_id" varchar(128) UNIQUE COLLATE "default", 16 | "name" varchar(128) COLLATE "default", 17 | "price" int, 18 | "state" int, 19 | "owner" varchar(128) COLLATE "default", 20 | "expend" jsonb NOT NULL, 21 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 22 | ) 23 | WITH (OIDS=FALSE); 24 | */ 25 | type T_Order struct { 26 | ID int64 `json:"id"` 27 | Type string `json:"type"` 28 | Channel string `json:"channel"` 29 | OrderId string `json:"order_id"` 30 | Name string `json:"name"` 31 | Price int `json:"price"` 32 | State int `json:"state"` 33 | Owner string `json:"owner,omitempty"` 34 | Expend *simplejson.Json `json:"expend"` 35 | CreatedAt time.Time `json:"created_at"` 36 | } 37 | const ( 38 | GOOD_ORDER_STATE_SUCCESS = 1 39 | GOOD_ORDER_STATE_WAITTING_PAY = 2 40 | GOOD_ORDER_STATE_PAY_FAILED = -1 41 | ) 42 | func GetOrderModel() (m.DbModel, error){ 43 | sc:=m.SqlController { 44 | TableName: "t_orders", 45 | InsertColumns: []string{"type","channel","order_id","name","price","state","owner","expend","created_at"}, 46 | QueryColumns: []string{"id","type","channel","order_id","name","price","state","owner","expend","created_at"}, 47 | InSertFields: insertOrderFields, 48 | QueryField2Obj: queryOrderField2Obj, 49 | } 50 | return m.GetModel(sc) 51 | } 52 | 53 | func insertOrderFields(obj interface{}) []interface{} { 54 | order :=obj.(T_Order) 55 | expend := []byte("{}") 56 | if order.Expend != nil { 57 | bs, err := order.Expend.MarshalJSON() 58 | if err==nil{ 59 | expend = bs 60 | } 61 | } 62 | return []interface{}{ 63 | order.Type, order.Channel, order.OrderId, order.Name, order.Price, order.State, order.Owner, expend, order.CreatedAt, 64 | } 65 | } 66 | 67 | func queryOrderField2Obj(fields []interface{}) interface{} { 68 | expend,_:=simplejson.NewJson(m.GetByteArr(fields[8])) 69 | order := T_Order{ 70 | ID: m.GetInt64(fields[0], 0), 71 | Type: m.GetString(fields[1]), 72 | Channel: m.GetString(fields[2]), 73 | OrderId: m.GetString(fields[3]), 74 | Name: m.GetString(fields[4]), 75 | Price: m.GetInt(fields[5], 0), 76 | State: m.GetInt(fields[6], 0), 77 | Owner: m.GetString(fields[7]), 78 | Expend: expend, 79 | CreatedAt: m.GetTime(fields[9], time.Now()), 80 | } 81 | return order 82 | } -------------------------------------------------------------------------------- /src/model/test/t_app_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | "../../common/conf" 7 | "github.com/bitly/go-simplejson" 8 | "time" 9 | m ".." 10 | "github.com/cjwddz/fast-model" 11 | ) 12 | 13 | func TestT_app(t *testing.T) { 14 | conf.Init("/home/cjwddz/桌面/git-project/golang-web/src/task.toml") 15 | md, err := m.GetAppModel() 16 | if err != nil { 17 | t.Fail() 18 | } 19 | // 正常插入 20 | app := m.T_app{ 21 | Icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAADhUlEQVRoQ+1aTUwTQRj9ZkvZtkARhGi0RBHDT6IBE0JMSKhCOMihSBC5ICoxJurVxJP8yJEYL0QTD0qACxKCePBCFCXpBQ4UgyEQFVSKVNTYSmmXdnfMNE6zbFpYoGVbXG7tPmbe+973vkk6iyDO/1Ao/o39XDWAUBRb2hhbdx07JOW0TkBDv+cMI8BTQHA0tsj/Y4NhXmDgam+d/g3lFxQQII9hJCaJS0gJCM5SEUEBjX2euZitvLSqGOa76/XZ5OuAgIY+7jyDhMF4qD7lKGCmpreefR4Q0NjnaQUELfEkADC0ddfrW1UBirmmOqBY6YNngpoBZT1QM6Bs/UE9B5Q24P92oOFUwigCwD0TfrPUCUuBxmpKZXzS7987BP24nS9YXQMjeSYXF9bp7U6hY+lotqWCzSULP7CuTdoWhULxJp0WdjKFRYV/ODyJEML0WXIiFGEMznuvOcenXzhXLi7iAu6YtaM56cxBsrBjRVi+O+wrDSXgcr933d4GLTg7LewKAOCmAc5EBWyGi7iArgs61/Qyb7O7MFQeTyi7MeR10bYgm4UjRp7dOq19W5KlMd9+6bW3VLA/iFNSAVLcshsOhxSxnRYyZzNjTcWJJW2vuNkVDgwdVaxp+IN/tHfCX0Y3kSOAkJaLi6gD96vYsRQW9l0f5AIZIJ/T9HCItMRmAkh2msvZA2s8dpD/DycgMwnsHed0yRQXMQGGRHA9qtYZxRUnk6T2hLZUHGZKzMVhG908SQsZGgaZMAZX+wj37eNPnCcXFzEBZHSSnuf8eIbjwUMXNrKo6Mtv3krDTImNL/DBnz4IdmpJ0I8t8PmrPkgVZ2UzXMQEPKll7X4B3O+WhEXxovmZTBoJIw3zRr0tZ1rJPuG3EmI6+wemfNYX0/y6sZmzH800l7N5tLViUkB7pdaalao5eXPIi2kLiCv1uIadTWDAsNF8l1ZWrtAdtxAJ70OLDn918lPSQ4suTvNBwnytWAvh5rsiLURO0LxMZs7uxBnf3Tg4LsVkxJgUHbiNLPJMLG784zBpPTm4HTsgO1S7DdxKiHebm6z9VAGyyhRFkOpAFIsra2nVAVlliiJIdSCKxZW19J5yIO4v+Yhll5555hHAEVn2KQzCAJ97LuoDl/F756KbqCG39QhDV6w6QSqPEVwJ+aqBuCsCmYixlz0EYGzkYlvavSHfVlG4xbe0/V/rXqJPNUQmQgAAAABJRU5ErkJggg==", 22 | AppId: "AGJLJWEFJ298R5pF", 23 | Name: "test_app", 24 | Version: "1.0", 25 | Describe: "a app for test", 26 | Developer: 15, 27 | Valid: true, 28 | File: "XXXXXXXXXXXXX", 29 | Src: "XXXXXX", 30 | Expend: simplejson.New(), 31 | DownloadCount: 124, 32 | CreatedAt: time.Now(), 33 | } 34 | err = md.Insert(app) 35 | if err != nil { 36 | fmt.Println(err.Error()) 37 | t.Fail() 38 | } 39 | cond := model.DbCondition{} 40 | rs, err := md.Query(cond.And2(">", "id", 0).Limit2(4, -1)) 41 | if err != nil { 42 | fmt.Println(err.Error()) 43 | t.Fail() 44 | } 45 | sq:=model.DbSetCondition{} 46 | err = md.Update(sq.And2("!=", "name", "old app").Set2("name","aaaaaaa")) 47 | if err != nil { 48 | fmt.Println(err.Error()) 49 | t.Fail() 50 | } 51 | err = md.Delete(cond.Reset().And2("!=", "name", "old app").And2(">","id",1000)) 52 | fmt.Println(rs) 53 | fmt.Println(md.CountAll()) 54 | fmt.Println(md.Count(cond)) 55 | } -------------------------------------------------------------------------------- /src/model/t_goods.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | m "github.com/cjwddz/fast-model" 6 | "github.com/bitly/go-simplejson" 7 | ) 8 | 9 | /* 10 | DROP TABLE IF EXISTS t_goods; 11 | CREATE TABLE t_goods ( 12 | "id" serial NOT NULL, 13 | "type" varchar(128), 14 | "channel" varchar(16) COLLATE "default", 15 | "name" varchar(128) COLLATE "default", 16 | "price" int, 17 | "state" int, 18 | "owner" varchar(128) COLLATE "default", 19 | "count" int DEFAULT -1, 20 | "expend" jsonb NOT NULL, 21 | "update_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP, 22 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 23 | ) 24 | WITH (OIDS=FALSE); 25 | */ 26 | type T_Goods struct { 27 | ID int64 `json:"id"` 28 | Type string `json:"type"` 29 | Channel string `json:"channel"` 30 | Name string `json:"name"` 31 | Price int `json:"price"` 32 | State int `json:"state"` 33 | Owner string `json:"owner,omitempty"` 34 | Count int `json:"count"` 35 | Expend *simplejson.Json `json:"expend"` 36 | UpdateAt time.Time `json:"update_at"` 37 | CreatedAt time.Time `json:"created_at"` 38 | } 39 | const ( 40 | GOOD_TYPE_INVALID = -1 41 | GOOD_TYPE_VALID = 1 42 | ) 43 | func GetGoodModel() (m.DbModel, error){ 44 | sc:=m.SqlController { 45 | TableName: "t_goods", 46 | InsertColumns: []string{"type", "channel","name","price","state","owner","count","expend","update_at","created_at"}, 47 | QueryColumns: []string{"id","type","channel","name","price","state","owner","count","expend","update_at","created_at"}, 48 | InSertFields: insertGoodFields, 49 | QueryField2Obj: queryGoodField2Obj, 50 | } 51 | return m.GetModel(sc) 52 | } 53 | 54 | func insertGoodFields(obj interface{}) []interface{} { 55 | good :=obj.(T_Goods) 56 | expend := []byte("{}") 57 | if good.Expend != nil { 58 | bs, err := good.Expend.MarshalJSON() 59 | if err==nil{ 60 | expend = bs 61 | } 62 | } 63 | return []interface{}{ 64 | good.Type, good.Channel, good.Name, good.Price, good.State, good.Owner, good.Count, expend, good.UpdateAt, good.CreatedAt, 65 | } 66 | } 67 | func queryGoodField2Obj(fields []interface{}) interface{} { 68 | expend,_:=simplejson.NewJson(m.GetByteArr(fields[8])) 69 | good := T_Goods{ 70 | ID: m.GetInt64(fields[0], 0), 71 | Type: m.GetString(fields[1]), 72 | Channel: m.GetString(fields[2]), 73 | Name: m.GetString(fields[3]), 74 | Price: m.GetInt(fields[4], 0), 75 | State: m.GetInt(fields[5], 0), 76 | Owner: m.GetString(fields[6]), 77 | Count: m.GetInt(fields[7],0), 78 | Expend: expend, 79 | UpdateAt: m.GetTime(fields[9], time.Now()), 80 | CreatedAt: m.GetTime(fields[10], time.Now()), 81 | } 82 | return good 83 | } -------------------------------------------------------------------------------- /src/common/session.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "strconv" 9 | "../model" 10 | "time" 11 | "sync" 12 | ) 13 | 14 | type Session struct { 15 | User model.T_user 16 | Token string 17 | Time time.Time 18 | } 19 | // session过期时间,单位秒 20 | const TOKEN_TIME_OUT = 60*20 21 | var UserSessions = sync.Map{} 22 | /** 23 | 获取Session键 24 | */ 25 | func GetSessionID(account, osType, role string) string { 26 | raw:=fmt.Sprintf("%s-%s-%s", account, osType, role) 27 | h := md5.New() 28 | io.WriteString(h,raw) 29 | return fmt.Sprintf("%X", h.Sum(nil)) 30 | } 31 | /** 32 | 更新Time,Token并返回Token 33 | */ 34 | func (sess *Session)GetToken()string{ 35 | sess.Time = time.Now() 36 | crutime := sess.Time.Unix() 37 | h := md5.New() 38 | io.WriteString(h, sess.User.Role+sess.User.Email+strconv.FormatInt(crutime, 10)) 39 | sess.Token = fmt.Sprintf("%x", h.Sum(nil)) 40 | return sess.Token 41 | } 42 | /** 43 | 更新时间 44 | */ 45 | func (sess *Session)RefreshTime(){ 46 | sess.Time = time.Now() 47 | } 48 | /** 49 | 保存session 50 | */ 51 | func SaveSession(user model.T_user, osType string) (sessionKey string, sess *Session) { 52 | sessionKey = GetSessionID(user.Email,osType, user.Role) 53 | sess = new(Session) 54 | sess.User = user 55 | sess.GetToken() 56 | UserSessions.Store(sessionKey,sess) 57 | sess.RefreshTime() 58 | return 59 | } 60 | /** 61 | 移除session 62 | */ 63 | func RemoveSession(r *http.Request) { 64 | sessionId := r.FormValue("sessionId") 65 | UserSessions.Delete(sessionId) 66 | } 67 | /** 68 | 检测session合法性 69 | 包括cookies,get,post提交方式,检查超时 70 | */ 71 | func CheckSession(w http.ResponseWriter,r *http.Request) (err error) { 72 | sessionId:="" 73 | token:="" 74 | cookie0,err0 := r.Cookie("sessionId") 75 | if err0==nil{ 76 | sessionId= cookie0.Value 77 | } 78 | cookie1,err1 := r.Cookie("token") 79 | if err1==nil{ 80 | token= cookie1.Value 81 | } 82 | if sessionId!="" && token!=""{ 83 | //使用cookies包含了登录信息 84 | }else if r.Method == "GET" { 85 | sessionId = r.FormValue("sessionId") 86 | token = r.FormValue("token") 87 | } else { 88 | sessionId = r.PostFormValue("sessionId") 89 | token = r.PostFormValue("token") 90 | } 91 | session,ok:=UserSessions.Load(sessionId) 92 | if !ok || session==nil{ 93 | err = fmt.Errorf("用户session校验失败,session不存在!") 94 | return 95 | } 96 | sess:= session.(*Session) 97 | if sess.Token!=token{ 98 | err = fmt.Errorf("用户session校验失败,token不匹配,请重新登录!") 99 | return 100 | } 101 | during:=time.Now().Unix()-sess.Time.Unix() 102 | if during>TOKEN_TIME_OUT{ 103 | err = fmt.Errorf("用户session校验失败,登录超时,请重新登录!") 104 | return 105 | } 106 | // 更新登录时间 107 | sess.RefreshTime() 108 | http.SetCookie(w,&http.Cookie{Name:"sessionId",Value:sessionId,Path:"/"}) 109 | http.SetCookie(w,&http.Cookie{Name:"token",Value:token,Path:"/"}) 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /src/common/sms.go: -------------------------------------------------------------------------------- 1 | /** 2 | * 短信发送模块 3 | */ 4 | 5 | package common 6 | 7 | import ( 8 | "fmt" 9 | "math/rand" 10 | "time" 11 | "net/url" 12 | "crypto/md5" 13 | "strconv" 14 | "io" 15 | "strings" 16 | "crypto/hmac" 17 | "crypto/sha1" 18 | "encoding/base64" 19 | ) 20 | 21 | var SMS map[string]string 22 | 23 | func SendSMSCode(phone string) { 24 | if SMS == nil { 25 | SMS = map[string]string{} 26 | } 27 | rnd := rand.New(rand.NewSource(time.Now().UnixNano())) 28 | vcode := fmt.Sprintf("%06v", rnd.Int31n(1000000)) 29 | SMS[phone] = vcode 30 | 31 | crutime := time.Now() 32 | h := md5.New() 33 | io.WriteString(h, strconv.FormatInt(crutime.Unix(), 10)) 34 | nonce := fmt.Sprintf("%x", h.Sum(nil)) 35 | loc, err := time.LoadLocation("GMT") 36 | if err != nil { 37 | fmt.Println(err.Error()) 38 | } 39 | stime := time.Date(crutime.Year(),crutime.Month(),crutime.Day(),crutime.Hour(),crutime.Minute(),crutime.Second(),crutime.Nanosecond(),loc) 40 | fmt.Println(stime.Location()) 41 | accessKeyId := "LTAI1NsQHLR9jncU" 42 | accessKeySecret := "2lLDdD2MQdbFGCy3Uknr9QX5i2Nk5w" 43 | 44 | params := map[string]interface{}{} 45 | params["SignatureMethod"] = "HMAC-SHA1" 46 | params["SignatureNonce"] = nonce 47 | params["AccessKeyId"] = accessKeyId 48 | params["Timestamp"] = stime.Local().Format("2006-01-02T15:04:05Z") 49 | params["SignatureVersion"] = "1.0" 50 | params["Format"] = "xml" 51 | 52 | params["Action"]= "SendSms" 53 | params["Version"]= "2017-05-25" 54 | params["RegionId"]= "cn-hangzhou" 55 | params["PhoneNumbers"]= phone 56 | params["SignName"]= "阿里云短信测试专用" 57 | params["TemplateParam"]= "{\"customer\":\"test\"}" 58 | params["TemplateCode"]= "SMS_85630051" 59 | params["OutId"]= "123" 60 | 61 | i := 0 62 | res := "http://127.0.0.1:8888?" 63 | for k,v:=range params{ 64 | if i == 0 { 65 | res += fmt.Sprintf("%s=%v",k,v) 66 | }else{ 67 | res += fmt.Sprintf("&%s=%v",k,v) 68 | } 69 | i++ 70 | } 71 | a,err:=url.Parse(res) 72 | if err != nil { 73 | fmt.Println(err.Error()) 74 | return 75 | } 76 | result :=a.Query().Encode() 77 | strings.Replace(result,"+", "%20",-1) 78 | strings.Replace(result,"*", "%2A",-1) 79 | strings.Replace(result,"-", "%7E",-1) 80 | key := []byte(accessKeySecret+"&") 81 | mac := hmac.New(sha1.New, key) 82 | mac.Write([]byte(result)) 83 | input := fmt.Sprintf("%x", mac.Sum(nil)) 84 | final := base64.StdEncoding.EncodeToString([]byte(input)) 85 | params["Signature"] = final 86 | 87 | res="http://127.0.0.1:8888?" 88 | for k,v:=range params{ 89 | if i == 0 { 90 | res += fmt.Sprintf("%s=%v",k,v) 91 | }else{ 92 | res += fmt.Sprintf("&%s=%v",k,v) 93 | } 94 | i++ 95 | } 96 | a,err=url.Parse(res) 97 | if err != nil { 98 | fmt.Println(err.Error()) 99 | return 100 | } 101 | allResult :=a.Query().Encode() 102 | shit := "http://dysmsapi.aliyuncs.com/?"+allResult 103 | fmt.Println(shit) 104 | } 105 | -------------------------------------------------------------------------------- /src/app/goods.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "../common" 5 | "net/http" 6 | "../common/log" 7 | fm "github.com/cjwddz/fast-model" 8 | "../model" 9 | "fmt" 10 | "time" 11 | "strconv" 12 | "github.com/bitly/go-simplejson" 13 | ) 14 | 15 | /** 16 | 获取商品 17 | */ 18 | func GetGoods(sess *common.Session, w http.ResponseWriter, r *http.Request){ 19 | cond:=fm.DbCondition{}.And2(r,"=","i_type").And2(r,"=","s_channel").And2(r,"like","s_name").And2(r,"=","i_state") 20 | total,err:=GoodModel.Count(cond) 21 | if err!=nil{ 22 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "服务器内部出错!") 23 | log.E("GetGoods出错",sess.User.Email,err.Error()) 24 | return 25 | } 26 | result,err:=GoodModel.Query(cond.Limit2(r,"start","count").Order("order by created_at desc")) 27 | if err!=nil{ 28 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "服务器内部出错!") 29 | log.E("GetGoods出错",sess.User.Email,err.Error()) 30 | return 31 | } 32 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"goods": result,"total":total}) 33 | log.N("GetGoods",sess.User.Email,fmt.Sprintf("listCount=%d,total=%d",len(result),total)) 34 | return 35 | } 36 | 37 | /** 38 | 添加商品 39 | */ 40 | func AddGood(sess *common.Session, w http.ResponseWriter, r *http.Request){ 41 | if !common.IsRole(sess.User.Role,model.USER_ROLE_ADMIN){ 42 | log.W(common.ACTION_VIOLENCE,sess.User.Email,"该用户roles=%s,尝试添加商品.",sess.User.Role) 43 | common.ReturnEFormat(w,common.CODE_ROLE_INVADE, "抱歉,您没有添加商品的权限.") 44 | return 45 | } 46 | r.ParseForm() 47 | name:=r.Form.Get("name") 48 | channel:= r.Form.Get("channel") 49 | _price:=r.Form.Get("price") 50 | _state:=r.Form.Get("state") 51 | _count:=r.Form.Get("count") 52 | var state int 53 | if _state == "invalid"{ 54 | state = model.GOOD_TYPE_INVALID 55 | }else{ 56 | state = model.GOOD_TYPE_VALID 57 | } 58 | count,err:=strconv.Atoi(_count) 59 | if err!=nil || count< -1 { 60 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "请设置有效数量") 61 | return 62 | } 63 | price,err:=strconv.Atoi(_price) 64 | if err!=nil || price<=0 { 65 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "请设置有效价格") 66 | return 67 | } 68 | expend:= simplejson.New() 69 | expend.Set("desc",r.Form.Get("desc")) 70 | expend.Set("picture",r.Form.Get("picture")) 71 | typeStr:=sess.User.Email + "|" + strconv.FormatInt(time.Now().Unix(),32) 72 | good:=model.T_Goods{ 73 | Type: common.ToBase64([]byte(typeStr)), 74 | Channel: channel, 75 | Name: name, 76 | Price: price, 77 | State: state, 78 | Owner: sess.User.Email, 79 | Count: count, 80 | Expend: expend, 81 | UpdateAt: time.Now(), 82 | CreatedAt: time.Now(), 83 | } 84 | err=GoodModel.Insert(good) 85 | if err!=nil{ 86 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "服务器内部出错!") 87 | log.E("AddGood出错",sess.User.Email,err.Error()) 88 | return 89 | } 90 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"msg":"添加商品成功!"}) 91 | log.N("AddGood成功",sess.User.Email,"添加商品:channel=%s,name=%s,price=%s",channel,name,_price) 92 | } -------------------------------------------------------------------------------- /src/common/common.go: -------------------------------------------------------------------------------- 1 | /* 2 | 通用类 3 | */ 4 | package common 5 | 6 | import ( 7 | "crypto/md5" 8 | "encoding/json" 9 | "fmt" 10 | "net/http" 11 | "net/smtp" 12 | "regexp" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | type R struct { 18 | Code int `json:"code"` 19 | Data map[string]interface{} `json:"data"` 20 | } 21 | type RE struct { 22 | Code int `json:"code"` 23 | Msg string `json:"msg"` 24 | } 25 | /** 26 | api回应 27 | */ 28 | func ReturnFormat(w http.ResponseWriter, code int, data map[string]interface{}) { 29 | if data!=nil{ 30 | res := R{Code: code, Data: data} 31 | omg, _ := json.Marshal(res) 32 | w.Write(omg) 33 | } 34 | } 35 | /** 36 | 打印错误 37 | */ 38 | func ReturnEFormat(w http.ResponseWriter, code int, msg string) { 39 | res := RE{Code: code, Msg: msg} 40 | omg, _ := json.Marshal(res) 41 | //w.WriteHeader(code) 42 | w.Write(omg) 43 | } 44 | 45 | /** 46 | 获取md5 47 | */ 48 | func MD5Password(pwd string) string { 49 | data := []byte(pwd) 50 | has := md5.Sum(data) 51 | md5str1 := fmt.Sprintf("%x", has) //将[]byte转成16进制 52 | return md5str1 53 | } 54 | 55 | /** 56 | 获取页面限制条件 57 | */ 58 | func PageParams(r *http.Request) (limitStr string) { 59 | skip := 0 60 | limit := 20 61 | limitStr = "" 62 | page := r.FormValue("page") 63 | pageSize := r.FormValue("pageSize") 64 | pageInt, err := strconv.Atoi(page) 65 | if err == nil { 66 | pageSizeInt, err := strconv.Atoi(pageSize) 67 | if err == nil { 68 | limit = pageSizeInt 69 | skip = limit * (pageInt - 1) 70 | } 71 | } 72 | limitStr = fmt.Sprintf(" LIMIT %d OFFSET %d ", limit, skip) 73 | return 74 | } 75 | 76 | /** 77 | 匹配手机号 78 | */ 79 | func IsPhone(s string) bool { 80 | if regexp.MustCompile(`^(13[0-9]|14[0-9]|15[0-9]|17[0-9]|18[0-9])\d{8}$`).MatchString(s) { 81 | return true 82 | } 83 | return false 84 | } 85 | 86 | /** 87 | 匹配邮箱 88 | */ 89 | func IsEmail(s string) bool { 90 | if regexp.MustCompile("[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+").MatchString(s) { 91 | return true 92 | } 93 | return false 94 | } 95 | 96 | /** 97 | 是否包含某个角色 98 | */ 99 | func IsRole(userRoles string, role string) bool { 100 | roles := strings.Split(userRoles, ",") 101 | for _, r := range roles { 102 | if r == role { 103 | return true 104 | } 105 | } 106 | return false 107 | } 108 | 109 | /** 110 | 发送邮件通知 111 | */ 112 | func SendToMail() error { 113 | // Set up authentication information. 114 | userName:="baicaiplus@163.com" 115 | password:="baibaiasdfsssee2" 116 | host:="mail.163.com" 117 | auth := smtp.PlainAuth("", userName, password, host) 118 | to := []string{"1436983000@qq.com","cjwddz@gmail.com"} 119 | msg := []byte("To: recipient@example.net\r\n" + 120 | "Subject: discount Gophers!\r\n" + 121 | "\r\n" + 122 | "This is the email body.\r\n") 123 | err := smtp.SendMail("mail.163.com:25", auth, userName, to, msg) 124 | if err != nil { 125 | fmt.Printf(err.Error()) 126 | } 127 | return err 128 | } 129 | -------------------------------------------------------------------------------- /src/model/t_app.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | "github.com/bitly/go-simplejson" 6 | m "github.com/cjwddz/fast-model" 7 | ) 8 | 9 | /* 10 | DROP TABLE IF EXISTS t_app; 11 | CREATE TABLE t_app ( 12 | "id" serial NOT NULL, 13 | "icon" text, 14 | "app_id" varchar(16) COLLATE "default", 15 | "name" varchar(128) COLLATE "default", 16 | "version" varchar(16) COLLATE "default", 17 | "describe" varchar(255) COLLATE "default", 18 | "developer" varchar(128) COLLATE "default", 19 | "valid" bool DEFAULT TRUE, 20 | "file" varchar(256) COLLATE "default", 21 | "src" varchar(256) COLLATE "default", 22 | "expend" jsonb NOT NULL, 23 | "download_count" int4 DEFAULT -1, 24 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 25 | ) 26 | WITH (OIDS=FALSE); 27 | */ 28 | type T_app struct { 29 | ID int64 `json:"id"` 30 | Icon string `json:"icon"` 31 | AppId string `json:"app_id"` 32 | Name string `json:"name"` 33 | Version string `json:"version"` 34 | Describe string `json:"describe"` 35 | Developer string `json:"developer"` 36 | Valid bool `json:"valid"` 37 | File string `json:"file"` 38 | Src string `json:"src"` 39 | Expend *simplejson.Json `json:"expend"` 40 | DownloadCount int `json:"download_count"` 41 | CreatedAt time.Time `json:"created_at"` 42 | } 43 | 44 | func GetAppModel() (m.DbModel, error){ 45 | sc:=m.SqlController { 46 | TableName: "t_app", 47 | InsertColumns: []string{"icon","app_id","version","name","describe","developer","valid","file","src","expend","download_count","created_at"}, 48 | QueryColumns: []string{"id","icon","app_id","version","name","describe","developer","valid","file","src","expend","download_count","created_at"}, 49 | InSertFields: insertAppFields, 50 | QueryField2Obj: queryAppField2Obj, 51 | } 52 | return m.GetModel(sc) 53 | } 54 | 55 | func insertAppFields(obj interface{}) []interface{} { 56 | app:=obj.(T_app) 57 | expend := []byte("{}") 58 | if app.Expend != nil { 59 | bs, err := app.Expend.MarshalJSON() 60 | if err==nil{ 61 | expend = bs 62 | } 63 | } 64 | return []interface{}{ 65 | app.Icon,app.AppId,app.Version,app.Name,app.Describe,app.Developer,app.Valid,app.File,app.Src,expend,app.DownloadCount,app.CreatedAt, 66 | } 67 | } 68 | func queryAppField2Obj(fields []interface{}) interface{} { 69 | expend,_:=simplejson.NewJson(m.GetByteArr(fields[10])) 70 | app:=T_app{ 71 | ID:m.GetInt64(fields[0],0), 72 | Icon:m.GetString(fields[1]), 73 | AppId:m.GetString(fields[2]), 74 | Name:m.GetString(fields[4]), 75 | Version:m.GetString(fields[3]), 76 | Describe:m.GetString(fields[5]), 77 | Developer:m.GetString(fields[6]), 78 | Valid:m.GetBool(fields[7],true), 79 | File:m.GetString(fields[8]), 80 | Src:m.GetString(fields[9]), 81 | Expend:expend, 82 | DownloadCount:m.GetInt(fields[11],0), 83 | CreatedAt:m.GetTime(fields[12],time.Now()), 84 | } 85 | return app 86 | } -------------------------------------------------------------------------------- /src/model/t_user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | "github.com/bitly/go-simplejson" 6 | m "github.com/cjwddz/fast-model" 7 | ) 8 | 9 | /* 10 | DROP TABLE IF EXISTS t_user; 11 | CREATE TABLE t_user ( 12 | "id" serial NOT NULL, 13 | "account" varchar(128) COLLATE "default", 14 | "pwd" varchar(128) COLLATE "default", 15 | "role" varchar(16) COLLATE "default", 16 | "nick" varchar(128) COLLATE "default", 17 | "avatar" varchar(128) COLLATE "default", 18 | "phone" varchar(11) COLLATE "default", 19 | "email" varchar(128) COLLATE "default", 20 | "qq" varchar(13) COLLATE "default", 21 | "status" int4 DEFAULT 1, 22 | "expend" jsonb NOT NULL, 23 | "updated_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP, 24 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 25 | ) 26 | WITH (OIDS=FALSE); 27 | */ 28 | 29 | const ( 30 | USER_ROLE_COMMON = "common" 31 | USER_ROLE_DEVELOPER = "developer" 32 | USER_ROLE_ADMIN = "admin" 33 | USER_ROLE_SUPER = "super" 34 | USER_ROLE_EMPLOYER = "employer" 35 | 36 | USER_STATUS_INVALID=0 37 | USER_STATUS_WAITING_VERIFY = 2 38 | USER_STATUS_VALID=1 39 | 40 | ) 41 | // 用戶表 42 | type T_user struct { 43 | Id int64 `json:"id"` 44 | Pwd string `json:"pwd"` 45 | Role string `json:"role"` 46 | Nick string `json:"nick"` 47 | Avatar string `json:"avatar"` 48 | Phone string `json:"phone"` 49 | Email string `json:"email"` 50 | QQ string `json:"qq"` 51 | Status int `json:"status"` 52 | Expend * simplejson.Json `json:"expend"` 53 | UpdatedAt time.Time `json:"updated_at"` 54 | CreatedAt time.Time `json:"created_at"` 55 | } 56 | func GetUserModel() (m.DbModel, error){ 57 | sc:=m.SqlController { 58 | TableName: "t_user", 59 | InsertColumns: []string{"pwd","role","nick","avatar","phone","email","qq","status","expend","updated_at","created_at"}, 60 | QueryColumns: []string{"id","pwd","role","nick","avatar","phone","email","qq","status","expend","updated_at","created_at"}, 61 | InSertFields: insertUserFields, 62 | QueryField2Obj: queryUserField2Obj, 63 | } 64 | return m.GetModel(sc) 65 | } 66 | 67 | func insertUserFields(obj interface{}) []interface{} { 68 | user :=obj.(T_user) 69 | expend := []byte("{}") 70 | if user.Expend != nil { 71 | bs, err := user.Expend.MarshalJSON() 72 | if err==nil{ 73 | expend = bs 74 | } 75 | } 76 | return []interface{}{ 77 | user.Pwd, user.Role, user.Nick, user.Avatar, user.Phone, user.Email, user.QQ, user.Status, expend, user.UpdatedAt, user.CreatedAt, 78 | } 79 | } 80 | func queryUserField2Obj(fields []interface{}) interface{} { 81 | expend,_:=simplejson.NewJson(m.GetByteArr(fields[9])) 82 | user :=T_user{ 83 | Id:m.GetInt64(fields[0],0), 84 | Pwd:m.GetString(fields[1]), 85 | Role:m.GetString(fields[2]), 86 | Nick:m.GetString(fields[3]), 87 | Avatar:m.GetString(fields[4]), 88 | Phone:m.GetString(fields[5]), 89 | Email:m.GetString(fields[6]), 90 | QQ:m.GetString(fields[7]), 91 | Status:m.GetInt(fields[8],USER_STATUS_INVALID), 92 | Expend:expend, 93 | UpdatedAt:m.GetTime(fields[10],time.Now()), 94 | CreatedAt:m.GetTime(fields[11],time.Now()), 95 | } 96 | return user 97 | } -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Golang后台 2 | 3 | 该项在于实现一个较为完整的Golang后台. 4 | 5 | ## 目录结构 6 | 7 | ├── app ----------- 接口实现 8 | │   ├── apps.go 9 | │   ├── blog.go 10 | │   ├── file.go 11 | │   ├── manifest.go 12 | │   ├── publish.go 13 | │   ├── shop.go 14 | │   └── users.go 15 | ├── bak ----------- 配置文件备份 16 | │   ├── app.toml 17 | │   ├── init.sql 18 | │   └── public.sql 19 | ├── common ----------- 共用库 20 | │   ├── common.go 21 | │   ├── conf ----------- 配置模块 22 | │   │   ├── conf.go 23 | │   │   └── path.go 24 | │   ├── handle.go ----------- 请求接口模块 25 | │   ├── log ----------- 日志模块 26 | │   │   ├── logger.go 27 | │   │   └── logger_test.go 28 | │   │   29 | │   ├── RESULT_CODES.go ----------- 返回码常量 30 | │   ├── session.go ----------- Session模块 31 | │   ├── sms.go ----------- 短信模块 32 | │   ├── test_utils.go 33 | │   └── utils.go 34 | ├── main.go ----------- 主程序 35 | ├── model ----------- 数据模型 36 | │   ├── t_app.go 37 | │   ├── t_code.go 38 | │   ├── test 39 | │   │   ├── t_app_test.go 40 | │   │   ├── t_code_test.go 41 | │   │   ├── t_file_test.go 42 | │   │   ├── t_log_test.go 43 | │   │   ├── t_user_test.go 44 | │   │   └── t_vlog_test.go 45 | │   │   46 | │   ├── t_file.go 47 | │   ├── t_log.go 48 | │   ├── t_publish.go 49 | │   ├── t_user.go 50 | │   ── t_vlog.go 51 | └─ plugin ----------- 插件 52 |    ├── email ----------- 邮箱插件 53 |    │   └── email.go 54 |    ├── plugin.go 55 |    └── wx ----------- 微信机器人插件 56 |    ├── plugins 57 |    │   └── plugin.go 58 |    └── robot.go 59 | 60 | 61 | ## 添加接口 62 | 路由配置,其中Check指定是否做用户验证 63 | ```go 64 | routers:=[]common.BH{ 65 | // 用户登录/注销/注册 66 | {Url:"/api/login",Check:false,Handle:app.Login}, 67 | {Url:"/api/logout",Check:true,Handle:app.Logout}, 68 | {Url:"/api/register",Check:false,Handle:app.Register}, 69 | {Url:"/api/setUserAvatar",Check:true,Handle:app.SetUserAvatar}, 70 | // 发布任务 71 | {Url:"/api/publish",Check:true,Handle:app.Publish}, 72 | {Url:"/api/getTask",Check:false,Handle:app.GetTask}, 73 | // 文件校验/上传图片/上传文件 74 | {Url:"/api/checkSha256",Check:true,Handle:app.CheckSha256}, 75 | {Url:"/api/uploadFile",Check:true,Handle:app.UploadFile}, 76 | {Url:"/api/listFiles",Check:true,Handle:app.ListFiles}, 77 | {Url:"/api/deleteFile",Check:true,Handle:app.DeleteFile}, 78 | // App添加/删除/列表获取 79 | {Url:"/api/developer/add-app",Check:true,Handle:app.AddApp}, 80 | {Url:"/api/developer/list-apps",Check:true,Handle:app.ListApps}, 81 | } 82 | ``` 83 | 84 | ## 功能实现 85 | 86 | - [x] session 87 | - [x] 登录/注册/注销 88 | - [x] 日志系统 89 | - [x] 文件秒传 90 | - [x] 文件管理 91 | - [x] 发布任务 92 | - [ ] 任务管理 93 | - [ ] 个人信息修改 94 | 95 | - [ ] 邮箱系统/注册验证/任务推送 96 | - [ ] 微信机器人 97 | - [ ] redis 接口数据缓存 98 | - [ ] 支付系统 99 | - [ ] 充值系统 100 | 101 | ## 该项目不更新了~ 102 | -------------------------------------------------------------------------------- /src/model/t_publish.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | "github.com/bitly/go-simplejson" 6 | m "github.com/cjwddz/fast-model" 7 | ) 8 | 9 | /* 10 | DROP TABLE IF EXISTS t_publish; 11 | CREATE TABLE t_publish ( 12 | "id" serial NOT NULL, 13 | "state" int4 DEFAULT -1, 14 | "owner" text, 15 | "name" varchar(128) COLLATE "default", 16 | "describe" varchar(511) COLLATE "default", 17 | "money_lower" int4 DEFAULT -1, 18 | "money_upper" int4 DEFAULT -1, 19 | "outsourcing" bool DEFAULT TRUE, 20 | "labels" varchar(128) COLLATE "default", 21 | "commission" varchar(15) COLLATE "default", 22 | "need_code" bool DEFAULT FALSE, 23 | "annex" jsonb NOT NULL, 24 | "from_time" timestamp(6) DEFAULT CURRENT_TIMESTAMP, 25 | "to_time" timestamp(6) DEFAULT CURRENT_TIMESTAMP, 26 | "update_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP, 27 | "created_at" timestamp(6) DEFAULT CURRENT_TIMESTAMP 28 | ) 29 | WITH (OIDS=FALSE); 30 | */ 31 | type T_publish struct { 32 | ID int64 `json:"id"` 33 | Owner string `json:"owner"` 34 | Name string `json:"name"` 35 | Describe string `json:"describe"` 36 | MoneyLow int `json:"money_lower"` 37 | MoneyUp int `json:"money_upper"` 38 | OutSourcing bool `json:"outsourcing"` 39 | Labels string `json:"labels"` 40 | Commission string `json:"commission"` 41 | NeedCode bool `json:"need_code"` 42 | Annex *simplejson.Json `json:"annex"` 43 | FromTime time.Time `json:"from_time"` 44 | ToTime time.Time `json:"to_time"` 45 | UpdateTime time.Time `json:"update_at"` 46 | CreatedTime time.Time `json:"created_at"` 47 | } 48 | 49 | func GetPublishModel() (m.DbModel, error){ 50 | sc:=m.SqlController { 51 | TableName: "t_publish", 52 | InsertColumns: []string{"owner","name","describe","money_lower","money_upper","outsourcing","labels","commission","need_code","annex","from_time","to_time","update_at","created_at"}, 53 | QueryColumns: []string{"id","owner","name","describe","money_lower","money_upper","outsourcing","labels","commission","need_code","annex","from_time","to_time","update_at","created_at"}, 54 | InSertFields: insertPublishFields, 55 | QueryField2Obj: queryPublishField2Obj, 56 | } 57 | return m.GetModel(sc) 58 | } 59 | 60 | func insertPublishFields(obj interface{}) []interface{} { 61 | publish:=obj.(T_publish) 62 | Annex := []byte("{}") 63 | if publish.Annex != nil { 64 | bs, err := publish.Annex.MarshalJSON() 65 | if err==nil{ 66 | Annex = bs 67 | } 68 | } 69 | return []interface{}{ 70 | publish.Owner, publish.Name, publish.Describe, publish.MoneyLow, publish.MoneyUp, publish.OutSourcing, publish.Labels, publish.Commission, publish.NeedCode, Annex, publish.FromTime, publish.ToTime, publish.UpdateTime, publish.CreatedTime, 71 | } 72 | } 73 | func queryPublishField2Obj(fields []interface{}) interface{} { 74 | annex, _ := simplejson.NewJson(m.GetByteArr(fields[10])) 75 | publish := T_publish{ 76 | ID: m.GetInt64(fields[0], 0), 77 | Owner: m.GetString(fields[1]), 78 | Name: m.GetString(fields[2]), 79 | Describe: m.GetString(fields[3]), 80 | MoneyLow: m.GetInt(fields[4], 0), 81 | MoneyUp: m.GetInt(fields[5], 0), 82 | OutSourcing: m.GetBool(fields[6], true), 83 | Labels: m.GetString(fields[7]), 84 | Commission: m.GetString(fields[8]), 85 | NeedCode: m.GetBool(fields[9], false), 86 | Annex: annex, 87 | FromTime: m.GetTime(fields[11], time.Now()), 88 | ToTime: m.GetTime(fields[12], time.Now()), 89 | UpdateTime: m.GetTime(fields[13], time.Now()), 90 | CreatedTime: m.GetTime(fields[14], time.Now()), 91 | } 92 | return publish 93 | } -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./common/conf" 5 | "fmt" 6 | "net/http" 7 | "./app" 8 | "./common" 9 | "./common/log" 10 | "html/template" 11 | "io/ioutil" 12 | "strings" 13 | "os" 14 | "./plugin" 15 | ) 16 | 17 | func main() { 18 | // 初始化配置文件 19 | conf.Init("./app.toml") 20 | // 初始化模型 21 | app.Init() 22 | // 初始化日志系统 23 | log.Init() 24 | // 初始化插件 25 | go plugin.Init() 26 | defer func() { 27 | log.Flush() 28 | }() 29 | BeginServer() 30 | } 31 | 32 | func BeginServer(){ 33 | InitTemplate() 34 | /**注册路由*/ 35 | routers:=[]common.BH{ 36 | // 用户登录/注销/注册 37 | {Url:"/api/login",Check:false,Handle:app.Login}, 38 | {Url:"/api/logout",Check:true,Handle:app.Logout}, 39 | {Url:"/api/register",Check:false,Handle:app.Register}, 40 | {Url:"/api/setUserAvatar",Check:true,Handle:app.SetUserAvatar}, 41 | // 发布任务 42 | {Url:"/api/publish",Check:true,Handle:app.Publish}, 43 | {Url:"/api/getTask",Check:false,Handle:app.GetTask}, 44 | // 文件校验/上传图片/上传文件 45 | {Url:"/api/checkSha256",Check:true,Handle:app.CheckSha256}, 46 | {Url:"/api/uploadFile",Check:true,Handle:app.UploadFile}, 47 | {Url:"/api/listFiles",Check:true,Handle:app.ListFiles}, 48 | {Url:"/api/deleteFile",Check:true,Handle:app.DeleteFile}, 49 | // App添加/删除/列表获取 50 | {Url:"/api/developer/addApp",Check:true,Handle:app.AddApp}, 51 | {Url:"/api/developer/listApps",Check:true,Handle:app.ListApps}, 52 | // 商品和交易 53 | {Url:"/api/pay",Check:true,Handle:app.Pay}, 54 | {Url:"/api/getGoods",Check:true,Handle:app.GetGoods}, 55 | {Url:"/api/addGood",Check:true,Handle:app.AddGood}, 56 | {Url:"/api/getOrders",Check:true,Handle:app.GetOrders}, 57 | {Url:"/api/pay/notify",Check:true,Handle:func(sess *common.Session,w http.ResponseWriter, r *http.Request){ 58 | r.ParseForm() 59 | fmt.Printf("notify_callback!!") 60 | fmt.Printf(fmt.Sprintf("%v",r.Form)) 61 | log.N("NOTIFY","",fmt.Sprintf("%v",r.Form)) 62 | }}, 63 | //{Url:"/api/developer/add-code",Check:true,Handle2:app.AddCode}, 64 | //{Url:"/api/developer/list-codes",Check:true,Handle2:app.ListCodes}, 65 | } 66 | common.SetRouters(routers) 67 | fmt.Println("开始服务!") 68 | err:=http.ListenAndServe(fmt.Sprintf(":%s", conf.App.ServerPort), nil) 69 | if err!=nil{ 70 | fmt.Println("服务退出!"+err.Error()) 71 | } 72 | } 73 | 74 | /** 初始化模板*/ 75 | func InitTemplate(){ 76 | /**获取模板,设置页面*/ 77 | index,err:=getTemplate() 78 | if err!=nil{ 79 | fmt.Println(fmt.Sprintf("设置模板出错!原因:%s",err.Error())) 80 | } 81 | handler:=func(writer http.ResponseWriter, request *http.Request) { 82 | writer.Header().Set("Content-Type", "text/html;charset=utf-8") 83 | if request.Method == "GET"{ 84 | err:=index.Execute(writer, nil) 85 | if err!=nil{ 86 | fmt.Println(err.Error()) 87 | http.Redirect(writer,request,"/404.html",http.StatusFound) 88 | } 89 | return 90 | } 91 | http.Redirect(writer,request,"/404.html",http.StatusFound) 92 | } 93 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 94 | path:=conf.App.StaticPath + r.URL.String() 95 | f,err := os.Stat(path) 96 | if err != nil{ // 不存在路径 97 | handler(w,r) 98 | return 99 | } 100 | if !f.IsDir() { // 文件直接提供 101 | http.FileServer(http.Dir(conf.App.StaticPath)).ServeHTTP(w,r) 102 | return 103 | } 104 | if _,err := os.Stat(path + "/index.html");err==nil { 105 | http.FileServer(http.Dir(conf.App.StaticPath)).ServeHTTP(w,r) 106 | }else { // 返回找不到,否则会列出全部静态目录 107 | handler(w,r) 108 | } 109 | }) 110 | // 更新模板文件 111 | http.HandleFunc("/resetTemplate", func(writer http.ResponseWriter, request *http.Request) { 112 | index,err=getTemplate() 113 | if err!=nil{ 114 | rs:=fmt.Sprintf("设置模板出错!原因:%s",err.Error()) 115 | writer.Write([]byte(rs)) 116 | return 117 | } 118 | rs:=fmt.Sprintf("更新成功,入口模板为%s",index.Name()) 119 | writer.Write([]byte(rs)) 120 | return 121 | }) 122 | } 123 | 124 | /** 125 | 获取模板入口 126 | */ 127 | func getTemplate() (*template.Template,error){ 128 | /*从静态目录中加载并设置模板*/ 129 | fmt.Println(fmt.Sprintf("从[%s]中加载模板..",conf.App.StaticPath)) 130 | files, err := ioutil.ReadDir(conf.App.StaticPath) 131 | if err != nil { 132 | return nil,err 133 | } 134 | var htmlFiles []string 135 | for _, file := range files { 136 | fileName := file.Name() 137 | if strings.HasSuffix(fileName, ".html") { 138 | htmlFiles = append(htmlFiles, conf.App.StaticPath+"/"+fileName) 139 | fmt.Println(fmt.Sprintf("添加模板:%s",fileName)) 140 | } 141 | } 142 | if len(htmlFiles)==0{ 143 | return nil,fmt.Errorf("在%s中找不到模板文件",conf.App.StaticPath) 144 | } 145 | templates:=template.Must(template.ParseFiles(htmlFiles...)) 146 | /*设置前端入口模板*/ 147 | index:=templates.Lookup("index.html") 148 | return index,nil 149 | } -------------------------------------------------------------------------------- /src/app/pay.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "../common" 5 | "net/http" 6 | "sync" 7 | "../model" 8 | "../common/log" 9 | fm "github.com/cjwddz/fast-model" 10 | "time" 11 | "strconv" 12 | "github.com/bitly/go-simplejson" 13 | "fmt" 14 | ) 15 | var GoodLock sync.Mutex 16 | /** 17 | 购买支付接口 18 | */ 19 | func Pay(sess *common.Session, w http.ResponseWriter, r *http.Request) { 20 | r.ParseForm() 21 | returnUrl:=r.Form.Get("returnUrl") 22 | goodType:=r.Form.Get("goodType") 23 | count,err:= strconv.Atoi(r.Form.Get("count")) 24 | contact:=r.Form.Get("contact") 25 | if contact == ""{ 26 | contact = sess.User.Email 27 | } 28 | if err!=nil || count<0{ 29 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID, "数量输入非法") 30 | return 31 | } 32 | if count== 0{ 33 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID, "选择商品数量不能为0!") 34 | return 35 | } 36 | if goodType == ""{ 37 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID, "请提供商品类型") 38 | return 39 | } 40 | GoodLock.Lock() 41 | defer GoodLock.Unlock() 42 | result,err:=GoodModel.Query(fm.DbCondition{}.And("=","type",goodType).Limit(1,0)) 43 | if err!=nil{ 44 | log.E("Pay",sess.User.Email,"reason:%s",err.Error()) 45 | common.ReturnEFormat(w,common.CODE_DB_RW_ERR, "数据库查询出错") 46 | return 47 | } 48 | if result==nil || len(result)<=0{ 49 | log.W("Pay",sess.User.Email,"商品类型不存在,goodType=%s",goodType) 50 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID, "商品类型不存在") 51 | return 52 | } 53 | good:= result[0].(model.T_Goods) 54 | if good.State == model.GOOD_TYPE_INVALID{ 55 | log.W("Pay",sess.User.Email,"商品类型不可用,goodType=%s,goodName=%s,channel=%s",good.Type,good.Name,good.Channel) 56 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID, "该商品已经下架") 57 | return 58 | } 59 | orderId:=good.Type + common.BytesToInt([]byte(strconv.FormatInt(time.Now().Unix(),32))) + common.GetRandomInt(4) 60 | if good.Count != - 1 && good.Count < count { 61 | log.W("Pay",sess.User.Email,"商品库存数量不足,goodType=%s,goodName=%s,channel=%s,库存数量:%d,购买数量:%d",good.Type,good.Name,good.Channel,good.Count,count) 62 | common.ReturnEFormat(w,common.CODE_RESOURCE_SHORT, "商品数量不足") 63 | return 64 | } 65 | expend:= simplejson.New() 66 | expend.Set("count",count) 67 | expend.Set("payType", "alipay") 68 | expend.Set("contact",contact) 69 | // 提取用户附加信息 70 | order := model.T_Order{ 71 | Type: good.Type, 72 | Channel: good.Channel, 73 | OrderId: orderId, 74 | Name: good.Name, 75 | Price: good.Price * count, 76 | State: model.GOOD_ORDER_STATE_WAITTING_PAY, 77 | Owner: sess.User.Email, 78 | Expend: expend, 79 | CreatedAt: time.Now(), 80 | } 81 | err=OrderModel.Insert(order) 82 | if err!=nil{ 83 | log.E("Pay",sess.User.Email,"系统添加订单失败,reason:%s",err.Error()) 84 | common.ReturnEFormat(w,common.CODE_DB_RW_ERR, "生成订单失败!请稍后重试.") 85 | return 86 | } 87 | if good.Count!= - 1{ 88 | err=GoodModel.Update(fm.DbSetCondition{}.And("=","type",good.Type).Set("count",good.Count - count).Set("update_at",time.Now())) 89 | if err!=nil{ 90 | log.E("Pay",sess.User.Email,"严重错误!生成订单后更新数量出错!!reason:%s",err.Error()) 91 | } 92 | } 93 | // 提取用户附加信息 94 | url,err:=common.GetAliPayLink(float32(good.Price * count)/100,good.Name,orderId,returnUrl) 95 | if err!=nil{ 96 | log.E("Pay",sess.User.Email,"reason:%s",err.Error()) 97 | common.ReturnEFormat(w,common.CODE_SERVICE_ERR, "服务器出现错误.") 98 | return 99 | } 100 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"url":url,"msg":"操作成功!"}) 101 | } 102 | 103 | /** 104 | 获取订单 105 | */ 106 | func GetOrders(sess *common.Session, w http.ResponseWriter, r *http.Request){ 107 | if !common.IsRole(sess.User.Role,model.USER_ROLE_ADMIN){ 108 | log.W(common.ACTION_VIOLENCE,sess.User.Email,"该用户roles=%s,尝试查看订单.",sess.User.Role) 109 | common.ReturnEFormat(w,common.CODE_ROLE_INVADE, "抱歉,您没有查看订单的权限.") 110 | return 111 | } 112 | cond:=fm.DbCondition{}.And2(r,"=","i_type").And2(r,"=","s_channel").And2(r,"like","s_name").And2(r,"=","i_state").And2(r,"like","s_order_id") 113 | total,err:=OrderModel.Count(cond) 114 | if err!=nil{ 115 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "服务器内部出错!") 116 | log.E("GetOrders出错",sess.User.Email,err.Error()) 117 | return 118 | } 119 | result,err:=OrderModel.Query(cond.Limit2(r,"start","count").Order("order by created_at desc")) 120 | if err!=nil{ 121 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "服务器内部出错!") 122 | log.E("GetOrders出错",sess.User.Email,err.Error()) 123 | return 124 | } 125 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"orders": result,"total":total}) 126 | log.N("GetOrders",sess.User.Email,fmt.Sprintf("listCount=%d,total=%d",len(result),total)) 127 | return 128 | } 129 | -------------------------------------------------------------------------------- /src/app/publish.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "net/http" 5 | "../common" 6 | "../common/log" 7 | "../model" 8 | "strconv" 9 | "github.com/bitly/go-simplejson" 10 | fm "github.com/cjwddz/fast-model" 11 | "time" 12 | "fmt" 13 | ) 14 | 15 | func Publish(sess *common.Session, w http.ResponseWriter, r *http.Request){ 16 | if !common.IsRole(sess.User.Role,model.USER_ROLE_EMPLOYER){ 17 | log.W(common.ACTION_VIOLENCE,sess.User.Email,"该用户roles=%s,尝试发布任务.",sess.User.Role) 18 | common.ReturnEFormat(w,common.CODE_ROLE_INVADE, "抱歉,你不并是雇主,请先到个人信息页面修改角色.") 19 | return 20 | } 21 | r.ParseForm() 22 | name := r.PostFormValue("name") 23 | describe := r.PostFormValue("describe") 24 | commission := r.PostFormValue("commission") 25 | money_lower,_:=strconv.Atoi(r.PostFormValue("money_lower")) 26 | money_upper,_:=strconv.Atoi(r.PostFormValue("money_upper")) 27 | outsourcing := false 28 | if r.PostFormValue("outsourcing")=="true"{ 29 | outsourcing = true 30 | } 31 | types := r.PostFormValue("type") 32 | code := false 33 | if r.PostFormValue("code")=="true"{ 34 | code = true 35 | } 36 | annex,_ := simplejson.NewJson([]byte(r.PostFormValue("annex"))) 37 | from_time,_ := strconv.ParseInt(r.PostFormValue("from_time"),10,64) 38 | to_time,_ := strconv.ParseInt(r.PostFormValue("to_time"),10,64) 39 | if len(name)<3{ 40 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "项目名不能少于三个字符") 41 | return 42 | } 43 | if len(describe)<10{ 44 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "项目描述不能少于10个字符") 45 | return 46 | } 47 | if len(name)<3{ 48 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "项目名不能少于三个字符") 49 | return 50 | } 51 | if len(name)<3{ 52 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "项目名不能少于三个字符") 53 | return 54 | } 55 | if len(name)<3{ 56 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "项目名不能少于三个字符") 57 | return 58 | } 59 | if from_time>to_time{ 60 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "项目终止时间不能大于项目开始时间") 61 | return 62 | } 63 | if money_upper 512*1024*1024{ 120 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID,"上传的文件不能超过512M") 121 | return 122 | } 123 | 124 | filePath := conf.App.PathFile +"/"+ fileKey 125 | t, err := os.Create(filePath) 126 | if err != nil { 127 | common.ReturnEFormat(w, common.CODE_SERVICE_ERR, "服务器内部出错!") 128 | log.E("UploadFile出错",sess.User.Email,err.Error()) 129 | return 130 | } 131 | defer t.Close() 132 | if _, err := io.Copy(t, f); err != nil { 133 | common.ReturnEFormat(w, common.CODE_SERVICE_ERR, "服务器内部出错!") 134 | log.E("UploadFile出错",sess.User.Email,err.Error()) 135 | return 136 | } 137 | file:=model.T_File{ 138 | Key:fileKey, 139 | Type:h.Header.Get("Content-Type"), 140 | Size:h.Size, 141 | Name:h.Filename, 142 | Owner:sess.User.Email, 143 | CreatedAt:time.Now(), 144 | } 145 | err=FileModel.Insert(file) 146 | if err!=nil{ 147 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "服务器内部出错!") 148 | log.E("UploadFile出错",sess.User.Email,err.Error()) 149 | return 150 | } 151 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"key": fileKey,"msg":"success"}) 152 | log.N("UploadFile上传文件成功",sess.User.Email,h.Filename) 153 | return 154 | } 155 | -------------------------------------------------------------------------------- /src/common/log/logger.go: -------------------------------------------------------------------------------- 1 | /** 2 | 日志类 3 | */ 4 | package log 5 | 6 | import ( 7 | "fmt" 8 | "../../model" 9 | fm "github.com/cjwddz/fast-model" 10 | "time" 11 | "../conf" 12 | "runtime" 13 | "strings" 14 | ) 15 | 16 | const ( 17 | color_red = uint8(iota + 91) 18 | color_green 19 | color_yellow 20 | color_blue 21 | color_magenta //洋红 22 | ) 23 | 24 | /**是否插入数据库*/ 25 | var CONF_WRITE_TO_DB = true 26 | var FMT_OUT = true 27 | 28 | var logs []model.T_log 29 | var cache int 30 | var count int 31 | var logInterval int 32 | var lastTime int64 33 | var LogModel fm.DbModel 34 | func Init() { 35 | cache = conf.App.LogCache 36 | logInterval = conf.App.LogInterval 37 | if cache<=0{ 38 | cache=100 39 | } 40 | if logInterval<=0{ 41 | logInterval=10 42 | } 43 | lastTime = time.Now().Unix() 44 | count=0 45 | fmt.Println(fmt.Sprintf("log system->cache:%d,logInterval:%d,lastTime:%d",cache,logInterval,lastTime)) 46 | logs=make([]model.T_log,cache) 47 | l,err:=model.GetLogModel() 48 | if err!=nil{ 49 | fmt.Println(err.Error()) 50 | return 51 | } 52 | LogModel = l 53 | CONF_WRITE_TO_DB = conf.App.LogWriteDb 54 | FMT_OUT = conf.App.LogOutConsole 55 | } 56 | /** 57 | debug 58 | */ 59 | func D(tag string,operator string,msg ...interface{}) { 60 | var content=(msg[0]).(string) 61 | if len(msg)>1{ 62 | content=fmt.Sprintf(msg[0].(string),msg[1:]...) 63 | } 64 | if FMT_OUT{ 65 | fmt.Println(fmt.Sprintf("D[tag:%s,operator:%s,time:%s]>>%s",tag,operator,time.Now().Format("2006/01/02 15:04:05"),content)) 66 | } 67 | insertSystemLogs("debug",tag,operator,content) 68 | } 69 | /** 70 | warn 71 | */ 72 | func W(tag string,operator string,msg ...interface{}) { 73 | var content=(msg[0]).(string) 74 | if len(msg)>1{ 75 | content=fmt.Sprintf(msg[0].(string),msg[1:]...) 76 | } 77 | if FMT_OUT{ 78 | fmt.Println(fmt.Sprintf("W[tag:%s,operator:%s,time:%s]>>%s",tag,operator,time.Now().Format("2006/01/02 15:04:05"),content)) 79 | } 80 | insertSystemLogs("warn",tag,operator,content) 81 | } 82 | /** 83 | info 84 | */ 85 | func I(tag string,operator string,msg ...interface{}) { 86 | var content=(msg[0]).(string) 87 | if len(msg)>1{ 88 | content=fmt.Sprintf(msg[0].(string),msg[1:]...) 89 | } 90 | if FMT_OUT { 91 | fmt.Println(Blue(fmt.Sprintf("I[tag:%s,operator:%s,time:%s]>>%s", tag, operator, time.Now().Format("2006/01/02 15:04:05"), content))) 92 | } 93 | insertSystemLogs("info",tag,operator,content) 94 | } 95 | /** 96 | error 97 | */ 98 | func E(tag string,operator string,msg ...interface{}) { 99 | var content=(msg[0]).(string) 100 | if len(msg)>1{ 101 | content=fmt.Sprintf(msg[0].(string),msg[1:]...) 102 | } 103 | if FMT_OUT { 104 | fmt.Println(Red(fmt.Sprintf("E[tag:%s operator:%s %s] %s", tag, operator, time.Now().Format("2006/01/02 15:04:05"), content))) 105 | } 106 | insertSystemLogs("error",tag,operator,content) 107 | } 108 | /** 109 | normal 110 | */ 111 | func N(tag string,operator string,msg ...interface{}) { 112 | var content=(msg[0]).(string) 113 | if len(msg)>1{ 114 | content=fmt.Sprintf(msg[0].(string),msg[1:]...) 115 | } 116 | if FMT_OUT { 117 | fmt.Println(Green(fmt.Sprintf("N[tag:%s,operator:%s,time:%s]>>%s", tag, operator, time.Now().Format("2006/01/02 15:04:05"), content))) 118 | } 119 | insertSystemLogs("normal",tag,operator,content) 120 | } 121 | /** 122 | 提示用户 123 | */ 124 | func Notify(tag string,operator string,msg ...interface{}){ 125 | var content=(msg[0]).(string) 126 | if len(msg)>1{ 127 | content=fmt.Sprintf(msg[0].(string),msg[1:]...) 128 | } 129 | if FMT_OUT { 130 | fmt.Println(Magenta(fmt.Sprintf("Notify[tag:%s,operator:%s,time:%s]>>%s", tag, operator, time.Now().Format("2006/01/02 15:04:05"), content))) 131 | } 132 | // todo notify action 133 | insertSystemLogs("notify",tag,operator,content) 134 | } 135 | 136 | /** 137 | 写入数据库 138 | */ 139 | func insertSystemLogs(logType,tag, operator, content string) { 140 | if !CONF_WRITE_TO_DB{ 141 | return 142 | } 143 | if operator==""{ 144 | operator = "unKnown" 145 | } 146 | _,f,l,_:=runtime.Caller(2) 147 | cl:=fmt.Sprintf("%s line:%d",strings.TrimPrefix(f,conf.AppRootPath),l) 148 | tl := model.T_log{ 149 | Tag:tag, 150 | Type:logType, 151 | Caller:cl, 152 | Operator:operator, 153 | Content:content, 154 | CreatedAt:time.Now(), 155 | } 156 | logs[count]=tl 157 | count++ 158 | if count>=cache || int(time.Now().Unix()-lastTime)>logInterval{ 159 | Flush() 160 | } 161 | lastTime=time.Now().Unix() 162 | } 163 | 164 | /** 165 | 强制写出 166 | 使用事务批量写出日志和单条插入日志分别占用时间单位(100,200):20:13,51:34 167 | */ 168 | func Flush(){ 169 | for i:=0;i1{ 131 | common.ReturnEFormat(w,common.CODE_VERIFY_FAIL, "管理员角色不能和其它角色一起设置") 132 | return 133 | } 134 | } 135 | if nick == "" { 136 | common.ReturnEFormat(w,common.CODE_PARAMS_INVALID, "昵称不能为空!") 137 | return 138 | } 139 | log.I("register请求","","phone:%s,email:%s,role:%s,nick:%s",phone,email,role,nick) 140 | users, err := UserModel.Query(fm.DbCondition{}.Or("=","phone",phone).Or("=","email",email).Limit(1,-1)) 141 | if err==nil && len(users) > 0 { 142 | user:=users[0].(model.T_user) 143 | if user.Phone==phone{ 144 | common.ReturnEFormat(w, common.REGISTER_ACCOUNT_EXIST, "手机号已经被注册,不可重复注册.") 145 | log.I("register失败","","phone:%s,email:%s,role:%s,reason:%s",phone,email,role,"手机号已经被注册,不可重复注册.") 146 | }else if user.Email==email{ 147 | common.ReturnEFormat(w, common.REGISTER_ACCOUNT_EXIST, "所使用邮箱已经被注册,不可重复注册.") 148 | log.I("register失败","","phone:%s,email:%s,role:%s,reason:%s",phone,email,role,"所使用邮箱已经被注册,不可重复注册.") 149 | } 150 | return 151 | } 152 | status:= model.USER_STATUS_VALID 153 | if common.IsRole(role,model.USER_ROLE_ADMIN){ 154 | status = model.USER_STATUS_WAITING_VERIFY 155 | } 156 | user := model.T_user{ 157 | Role:role, 158 | Nick:nick, 159 | Pwd:pwd, 160 | Status:status, 161 | Avatar:"https://avatars2.githubusercontent.com/u/24471738?v=4&s=40", 162 | Phone:phone, 163 | Email:email, 164 | QQ:"", 165 | UpdatedAt:time.Now(), 166 | CreatedAt:time.Now(), 167 | } 168 | err = UserModel.Insert(user) 169 | if err != nil { 170 | log.E("register失败","","phone:%s,email:%s,role:%s,reason:%s",phone,email,role,err.Error()) 171 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "恭喜你找到个bug..创建新用户出错,劳烦转告下管理员!") 172 | return 173 | } 174 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"msg":"注册成功,请重新登录!"}) 175 | log.N("register成功","","注册成功=phone:%s,email:%s,role:%s",phone,email,role) 176 | } 177 | 178 | /** 179 | 修改 180 | todo 记得同步session中的用户 181 | */ 182 | func SetUserAvatar(sess *common.Session, w http.ResponseWriter, r *http.Request) { 183 | avatar := r.PostFormValue("avatar") 184 | if len(avatar) != 64 { 185 | common.ReturnEFormat(w, common.CODE_PARAMS_INVALID, "无效的hash值") 186 | return 187 | } 188 | err:=UserModel.Update(fm.DbSetCondition{}.And("=","email",sess.User.Email).Set("avatar",avatar)) 189 | if err != nil { 190 | log.E("setUserAvatar失败",sess.User.Email,"reason:%s",err.Error()) 191 | common.ReturnEFormat(w, common.CODE_DB_RW_ERR, "修改头像失败!") 192 | return 193 | } 194 | common.ReturnFormat(w, common.CODE_OK, map[string]interface{}{"msg":"修改头像成功!"}) 195 | log.N("setUserAvatar成功",sess.User.Email,"修改头像") 196 | } --------------------------------------------------------------------------------