diff --git a/.gitignore b/.gitignore index f2dd955..c8e2484 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out + +rubbish-class +logs/ +*.log +go.sum \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..786cb85 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +build: + go build . + +dev: + go run . \ No newline at end of file diff --git a/README.en.md b/README.en.md deleted file mode 100644 index b9cbd2e..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# rubbish-class - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index b71ea6b..bfbafb4 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ #### 介绍 {**以下是码云平台说明,您可以替换此简介** -码云是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用码云实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +垃圾分类 #### 软件架构 -软件架构说明 +* PC端文件下载 +* 微信小程序端用户与工作人员摄像上传并记分查询 #### 安装教程 diff --git a/admin/admin-handler.go b/admin/admin-handler.go new file mode 100644 index 0000000..b422e4e --- /dev/null +++ b/admin/admin-handler.go @@ -0,0 +1,16 @@ +package admin + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +//HomeHandle 基础验证试验 +//HTTP基本认证(Basic Authentication) +//"用户名+冒号+密码"用BASE64算法加密后的字符串放在http request 中的header的Authorization中发送给服务端 +//最终格式:Authorization: Basic BASE64("用户名+冒号+密码") +func HomeHandle(c *gin.Context) { + user := c.MustGet(gin.AuthUserKey).(string) + c.JSON(http.StatusOK, user) +} diff --git a/app/app-handler.go b/app/app-handler.go new file mode 100644 index 0000000..7813729 --- /dev/null +++ b/app/app-handler.go @@ -0,0 +1,119 @@ +package app + +import ( + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "strings" + + "github.com/gin-gonic/gin" + "github.com/prometheus/common/log" +) + +//Indexhandle index +func Indexhandle(c *gin.Context) { + +} + +//Code2SessionHandle 登录凭证校验,通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。 +//GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code +func Code2SessionHandler(c *gin.Context) { + jscode := c.Param("jscode") + result, err := wxa.Code2Session(jscode) + if err != nil { + log.Warn(err) + c.AbortWithStatusJSON(http.StatusBadRequest, err.Error()) + return + } + c.JSON(http.StatusOK, result) +} + +//HistoryVideoHandle 视频(文件)下载 +func HistoryVideoHandler(c *gin.Context) { + openid := c.Param("openid") + p := "./video/" + openid + files, err := ioutil.ReadDir(p) + if err != nil { + log.Warn(err) + c.AbortWithStatusJSON(http.StatusBadRequest, err.Error()) + return + } + var videos []string + for _, f := range files { + if f.IsDir() { + continue + } + if !strings.HasSuffix(f.Name(), ".mp4") { + continue + } + videos = append(videos, f.Name()) + } + c.JSON(http.StatusOK, gin.H{ + "videos": videos, + }) +} + +type newForm struct { + File *multipart.FileHeader `form:"file"` + Openid string `form:"openid"` +} + +//UploadfileAndFormHandle 上传带form +func UploadfileAndFormHandler(c *gin.Context) { + var data newForm + if err := c.ShouldBind(&data); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, err.Error) + return + } + + filepath, err := SaveVideoFileService(data.File, &data.Openid) + if err != nil { + log.Warn(err) + c.AbortWithStatusJSON(http.StatusBadRequest, err.Error) + return + } + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "data": gin.H{ + "filepath": filepath, + "openid": data.Openid, + }, + }) +} + +//UploadHandle 小程序中文件上传,正式环境中使用云存储 +func UploadHandler(c *gin.Context) { + header, err := c.FormFile("file") + if err != nil { + log.Warn(err) + c.AbortWithStatusJSON(http.StatusBadRequest, err.Error) + return + } + dst := header.Filename + src, err := header.Open() + if err != nil { + log.Warn(err) + c.AbortWithStatusJSON(http.StatusBadRequest, err.Error) + return + } + defer src.Close() + out, err := os.Create(`video/` + dst) + if err != nil { + log.Warn(err) + c.AbortWithStatusJSON(http.StatusBadRequest, err.Error) + return + } + defer out.Close() + n, err := io.Copy(out, src) + if err != nil { + log.Warn(err) + c.AbortWithStatusJSON(http.StatusBadRequest, err.Error) + return + } + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "data": n, + }) +} diff --git a/app/app-service.go b/app/app-service.go new file mode 100644 index 0000000..d438918 --- /dev/null +++ b/app/app-service.go @@ -0,0 +1,65 @@ +package app + +import ( + "io" + "mime/multipart" + "os" + "strings" + + "github.com/silenceper/wechat" + "github.com/silenceper/wechat/cache" + "github.com/silenceper/wechat/miniprogram" +) + +var ( + wxa *miniprogram.MiniProgram + //RedisHost redis地址用于小程序缓存,DB号10 + RedisHost *string +) + +//init先于主程序运行,如何使配置生效? +func init() { + // memCache := cache.NewRedis(&cache.RedisOpts{ + // Host: *RedisHost, + // Database: 10, + // MaxIdle: 2, + // MaxActive: 50, + // IdleTimeout: 60, + // }) + mem := cache.NewMemory() + appConfig := &wechat.Config{ + AppID: "wx49cced01eec31847", + AppSecret: "0090134e4a137554a271133d7f73e633", + Cache: mem, + } + wx := wechat.NewWechat(appConfig) + wxa = wx.GetMiniProgram() +} + +//SaveVideoFileService 文件上传服务 +func SaveVideoFileService(file *multipart.FileHeader, openid *string) (*string, error) { + p := "./video/" + *openid + _, err := os.Stat(p) + if err != nil { + if os.IsNotExist(err) { + os.Mkdir(p, os.ModePerm) + os.Chmod(p, 0755) + } + } + src, err := file.Open() + if err != nil { + return nil, err + } + defer src.Close() + //创建 dst 文件 + fn := strings.TrimLeft(file.Filename, "tmp_") + out, err := os.Create(p + `/` + fn) + if err != nil { + return nil, err + } + defer out.Close() + // 拷贝文件 + _, err = io.Copy(out, src) + filename := out.Name() + return &filename, err +} diff --git a/app/readme.md b/app/readme.md new file mode 100644 index 0000000..dad83a4 --- /dev/null +++ b/app/readme.md @@ -0,0 +1,2 @@ +# 微信小程序 + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4369919 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module yyjishu.com/rubbish-class + +go 1.13 + +require ( + github.com/gin-gonic/gin v1.5.0 + github.com/prometheus/common v0.9.1 + github.com/silenceper/wechat v1.2.3 +) diff --git a/server.go b/server.go new file mode 100644 index 0000000..25aea84 --- /dev/null +++ b/server.go @@ -0,0 +1,52 @@ +package main + +import ( + "flag" + "net/http" + "os" + + "github.com/gin-gonic/gin" + "yyjishu.com/rubbish-class/admin" + "yyjishu.com/rubbish-class/app" +) + +var ( + endpoint = flag.String("s", "localhost:8080", "service endpoint") +) + +func main() { + flag.Parse() + + //启动时检查与创建文件夹video,用于存放用户上传的视频文件 + _, err := os.Stat("video") + if err != nil { + if os.IsNotExist(err) { + os.Mkdir("./video", os.ModePerm) + os.Chmod("./video", 0755) + } + } + + r := gin.Default() + + r.Use(gin.Recovery()) + r.StaticFS(`/video`, http.Dir("./video")) + + authorized := r.Group(`/admin`, gin.BasicAuth(gin.Accounts{ + "foo": "bar", + "admin": "admin", + })) + { + authorized.GET(`/index`, admin.HomeHandle) + } + + appGroup := r.Group(`/app`) + { + appGroup.POST(`/index`, app.Indexhandle) + appGroup.GET(`/code2session/:jscode`, app.Code2SessionHandler) + appGroup.POST(`/upload`, app.UploadHandler) + appGroup.POST(`/uploadvideo`, app.UploadfileAndFormHandler) + appGroup.GET(`/history/:openid`, app.HistoryVideoHandler) + } + + r.Run(*endpoint) +} diff --git a/service.rest b/service.rest new file mode 100644 index 0000000..d35d40f --- /dev/null +++ b/service.rest @@ -0,0 +1,89 @@ +@url=http://localhost:8080 + +//@url=https://api.xintijiao.com + +@token=31_uHgfsKEOrH5l3EUXM_nb_u_lTL2fRwPzxGbgvOlbM9PTGHxsH9hBzvnnP-SKOf59fb0SdUwrs-wcpTKfd_I99g +@refresh_token=31_T-96lys-lZtR4B9OtRMkFOqyX_zxJNwMH6vcKxenk7enK5jeDZ6Av-Ipj9aVWr1dzdsMaCACoxbmEFKmReHwTgtQNrOpY3TdiTkM5pEyvIQ +@openid=oYmRQxLw6UKdlQsZYIkRKbWlCijI + +############################### 以下微信小程序 ######################################## + +@jscode={{$guid}} + +GET {{url}}/app/code2session/{{jscode}} HTTP/1.1 + +### 视频历史下载 +GET {{url}}/app/history/ozgDT5KzARnML4khh70BEypaLlf8 HTTP/1.1 + +### 文件上传 +POST {{url}}/app/upload HTTP/1.1 +Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW + +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="text" + +title +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="file"; filename="readme.txt" +Content-Type: text/plain + +dsfsdfdsfsdaf 这里就是文件内容了! +t={{$timestamp}}&a={{$guid}} +------WebKitFormBoundary7MA4YWxkTrZu0gW-- + +### 视频上传 +POST {{url}}/app/uploadvideo HTTP/1.1 +Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW + +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="openid" + +ddddddd +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="age" + +12 +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="file"; filename="readme12.txt" +Content-Type: text/plain + +这里就是文件内容了! +t={{$timestamp}}&a={{$guid}} +------WebKitFormBoundary7MA4YWxkTrZu0gW-- + +############################ 以下网页基础认证 #################################### + +### admin http基本认证测试,以下二种方式均可 +GET {{url}}/admin/index HTTP/1.1 +Authorization: Basic admin admin +//Authorization: Basic YWRtaW46YWRtaW4= //BASE64处理后方式 + +######################## 以下微信服务号及订阅号 ############################### + +### wechat 验证 +GET {{url}}/wechat/index?signature=cc31b8d03faab03a06d7d9fba9d2fb011650680e&echostr=2755367349555518703×tamp=1583570152&nonce=1947789919 + +### wechat消息回调 +POST {{url}}/wechat/index HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +#### wechat获取用户accesstoken +GET {{url}}/wechat/access_token/081sBRac1YXCqz0H7Abc1K20bc1sBRaM HTTP/1.1 + +### wechat获取用户信息 +GET {{url}}/wechat/userinfo/{{token}}/{{openid}} HTTP/1.1 + +### wechat jssdk配置获取 +GET {{url}}/wechat/jssdk?url=http%3A%2F%2F127.0.0.1%3A5500%2Fstatic%2Fsdk.html HTTP/1.1 + +######################### 以下开放平台 ################################### + +### open 获取用户token +GET {{url}}/open/access_token/001HbLN007pXLJ1B4NO002nUN00HbLN6 HTTP/1.1 + +### open 刷新token +GET {{url}}/open/refresh_token/{{refresh_token}} HTTP/1.1 + +### open 获取用户信息 +GET {{url}}/open/userinfo?token=31_mhulwBPAd-H0f8Flj6DKUZ1mfiKxlydpC0rt5bq43mPgHCaHCiZbux9C_170hu3JswLHQMxZp0xMNTXA0znpluZV3R4q_yhYf7I5BnAskSs&openid=o9DpN1RQlkdgEXM2wWGvYNo4AZmA HTTP/1.1 +token: {{token}} diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..70680a1 --- /dev/null +++ b/static/index.html @@ -0,0 +1,12 @@ + + + + + + 微信通用框架-网页登录 + + + snsapi_login授权 + + + \ No newline at end of file diff --git a/static/js/site.js b/static/js/site.js new file mode 100644 index 0000000..c0b87b1 --- /dev/null +++ b/static/js/site.js @@ -0,0 +1,27 @@ +const url='https://api.xintijiao.com'; +const tokenname="token"; + +function GetQueryString(name) { + var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); + var r = window.location.search.substr(1).match(reg); + if(r != null) + return unescape(r[2]); + return null; +} + +function getUserToken(code) { + if(code != null){ + $.ajaxSettings.async = false; + $.getJSON(url + '/open/access_token/' + code, function(data,ok) { + if(ok=='success') { + sessionStorage.setItem(tokenname, JSON.stringify(data)); + } + }); + $.ajaxSettings.async = true; + } + + var userstr=sessionStorage.getItem(tokenname); + if (userstr!=null){ + return JSON.parse(userstr); + } +} \ No newline at end of file diff --git a/static/main.html b/static/main.html new file mode 100644 index 0000000..1770ae3 --- /dev/null +++ b/static/main.html @@ -0,0 +1,52 @@ + + + + + + admin + + +
+

code: {{code}}

+
+

access_token: {{usertoken.access_token}}

+

openid: {{usertoken.openid}}

+
+ +

nickname: {{user.nickname}}

+
+ + + + + + \ No newline at end of file diff --git a/static/mobile.html b/static/mobile.html new file mode 100644 index 0000000..bfc78cb --- /dev/null +++ b/static/mobile.html @@ -0,0 +1,52 @@ + + + + + + 微信通用框架 + + +
+

code: {{code}}

+
+

openid: {{usertoken.openid}}

+

access token: {{usertoken.access_token}}

+
+ +

nickname: {{user.nickname}}

+

province: {{user.province}}

+

city: {{user.city}}

+
+ + + + + + \ No newline at end of file diff --git a/static/sdk.html b/static/sdk.html new file mode 100644 index 0000000..809912b --- /dev/null +++ b/static/sdk.html @@ -0,0 +1,55 @@ + + + + + + JSSDK + + + +
+

注意此页面功能要在微信开发工具中进行调试!!!

+
+ + + + + + \ No newline at end of file diff --git a/static/sign.html b/static/sign.html new file mode 100644 index 0000000..2442e4a --- /dev/null +++ b/static/sign.html @@ -0,0 +1,12 @@ + + + + + + 微信通用框架 + + + snsapi_base授权 + snsapi_userinfo授权 + + \ No newline at end of file