- 后台由golang实现
- 技术栈
- 先睹为快
- 系统架构
- 项目结构
- 项目启动流程
- 功能特点举例
- 项目架构
- websocket通知新订单
- 邮件通知用户
- 定时器自动取消订单
golang版本1.17
技术栈mysql 5.7
redis
golang
negroni
jwt
gorilla mux
websocket
applet: 使用网上开源项目模板改造
后台:
applet:
1.linux 安装nginx: nginx申请ssl证书,配置https,将服务打到上游的golang web server服务
2.后台管理功能使用golang html template技术
3.applet功能,前后端分离,使用json数据格式交互
启动入口代码如下:
func main() { // 初始化配置 common.StartUp() // 初始化路由 router := routers.InitRoutes() //n := negroni.Classic() //n.UseHandler(router) // 减少 negroni 日志的打印 recovery := negroni.NewRecovery() recovery.Logger = common.Error n := negroni.New(recovery, negroni.Wrap(router)) server := &http.Server{ Addr: common.AppConfig.Server, Handler: n, } common.Info.Println("Listening on:", server.Addr) server.ListenAndServe() }
初始化配置部分如下:
func loadAppConfig() { file, err := os.Open(filepath.Join(CurDir, "common/config.json")) if err != nil { log.Fatalf("[loadConfig]:%sn", err) } defer file.Close() AppConfig = configuration{} err = json.NewDecoder(file).Decode(&AppConfig) if err != nil { log.Fatalf("[loadAppConfig]:%sn", err) } initDBConfig() initRedisConfig(AppConfig.RedisConfig) InitLog() }
初始化路由部分如下:
func InitRoutes() *mux.Router { router := mux.NewRouter().StrictSlash(false) // 静态文件映射 router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("views/static/")))) // applet 接口模块开始---------------------- // 首页 router = applet.SetIndexRoutes(router) // 分类 router = applet.SetCatalogRoutes(router) // ... 其它模块 // -----------------applet接口结束------------------------- // -----------------后台接口开始------------------------- // 首页 router = SetIndexRoutes(router) // banner router = SetBannerRoutes(router) // ... 其它模块 // -----------------后台接口结束------------------------- return router } // 后台banner 路由设置(完整) func SetBannerRoutes(router *mux.Router) *mux.Router { bannerRouter := mux.NewRouter() subrouter := bannerRouter.PathPrefix("/commerce/api/v2/banner").Subrouter() subrouter.HandleFunc("/list", controllers.ListBanner).Methods("GET") subrouter.HandleFunc("/addUI", controllers.AddBannerUIHandler).Methods("GET") subrouter.HandleFunc("/updateUI", controllers.GetBannerHandler).Methods("GET") subrouter.HandleFunc("/addOrEdit", controllers.AddOrEditBannerHandler).Methods("POST") subrouter.HandleFunc("/updateStatus", controllers.UpdateBannerStatusHandler).Methods("POST") router.PathPrefix("/commerce/api/v2/banner").Handler(negroni.New( // 拦截器:校验授权 negroni.HandlerFunc(common.Authorize), negroni.Wrap(bannerRouter), )) return router } // bannerController func init() { common.Templates["banner"] = template.Must(template.ParseFiles(filepath.Join(common.CurDir, "views/templates/banner.html"))) common.Templates["banner_edit"] = template.Must(template.ParseFiles(filepath.Join(common.CurDir, "views/templates/banner_edit.html"))) } // ... func GetBannerHandler(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(r.FormValue("id")) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } c, err := data.GetBannerById(id) if err != nil { common.Templates["5xx"].Execute(w, err) return } common.Templates["banner_edit"].Execute(w, c) } // applet indexController // 人气推荐商品接口 func ListHotGoods(w http.ResponseWriter, r *http.Request) { var res vo.IndexHotGoodsVo skuList, err := data.ListHotGoodsSku() if err != nil { common.Error.Printf("ListHotGoods err:%v", err) j, _ := json.Marshal(vo.Err(err.Error())) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(j) return } var voList []vo.AppSku for _, k := range skuList { voList = append(voList, vo.AppSku{Id: k.Id, Name: k.Name, MainImg: k.MainImg, RetailPrice: k.RealPrice, Desc: k.Description}) } res.HotGoodsList = voList j, _ := json.Marshal(vo.Ok(res)) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(j) }
配置
{ "Server" : ":9088", "SqlConfig" : { "DBName":"功能特点举例 项目架构", "Addr":"<你的mysql地址>:3306", "User":"<用户名>", "Passwd":"<密码>", "Net": "tcp", "AllowNativePasswords": true }, "ImgUploadPath": "views/imgs/", // 使用了oss后这行没有用,记得删除我(注释,不然会报错) "cookieDomain" : "localhost", "OssConfig" : { "Endpoint" : "<>", "AccessKeyId" : "<>", "AccessKeySecret" : "<>", "BucketName" : "<>" }, "RedisConfig" : { "MaxIdle" : 10, "MaxActive": 0, "IdleTimeout": 10, "Addr" : "<>:6379", "Db" : 0, "Pwd" : "<>" }, "LogConfig" : { "FileName" : "j.log", "MaxBytes" : 104857600, "BackUpCount": 2 } }
- 目前是单体应用,应付小规模的商家足够了,后续可能会考虑douyu-jupiter微服务或者dubbo-go服务
- 整个项目打包后只有10M大小,很轻量(不包括静态html, static文件)
- 静态文件static目录可以放在nginx相应目录,即动静分离
- 管理后台页面和go server保持websocket链接(heartbeat)
- 当有新订单时,server端发送一个标识数据到管理后台浏览器端,html5页面播放一段提示音提醒商家来新订单啦~
用户注册、找回密码、下单成功 等业务场景时,需要发送邮件给用户,我这里使用golang sdk内置的go mail功能,减少依赖
定时器自动取消订单用户下单后产生定时任务(golang自身定时器),15分钟内未支付时,自动取消订单并回滚相应的sku库存
源码
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)