Browse Source

import from internal gitlab

master
nick.sun 2 years ago
parent
commit
4cbf048e0e
  1. 9
      .gitignore
  2. 4
      bin/build.cmd
  3. 5
      bin/build.sh
  4. 4
      bin/test.cmd
  5. 5
      bin/test.sh
  6. 8
      src/loreal.com/.gitignore
  7. 24
      src/loreal.com/.vscode/launch.json
  8. 10
      src/loreal.com/dit/.editorconfig
  9. 39
      src/loreal.com/dit/.gitignore
  10. 180
      src/loreal.com/dit/cmd/WechatAppHost/app.go
  11. 66
      src/loreal.com/dit/cmd/WechatAppHost/config.default.go
  12. 79
      src/loreal.com/dit/cmd/WechatAppHost/config.go
  13. 49
      src/loreal.com/dit/cmd/WechatAppHost/db.go
  14. 73
      src/loreal.com/dit/cmd/WechatAppHost/endpoints.entry.go
  15. 223
      src/loreal.com/dit/cmd/WechatAppHost/endpoints.go
  16. 51
      src/loreal.com/dit/cmd/WechatAppHost/endpoints.report.go
  17. 123
      src/loreal.com/dit/cmd/WechatAppHost/logic.db.go
  18. 188
      src/loreal.com/dit/cmd/WechatAppHost/logic.go
  19. 86
      src/loreal.com/dit/cmd/WechatAppHost/logic.report.go
  20. 27
      src/loreal.com/dit/cmd/WechatAppHost/logic.task.maintenance.go
  21. 56
      src/loreal.com/dit/cmd/WechatAppHost/main.go
  22. 25
      src/loreal.com/dit/cmd/WechatAppHost/message.go
  23. 14
      src/loreal.com/dit/cmd/WechatAppHost/task.register.go
  24. 16
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/.vscode/launch.json
  25. BIN
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/api-tests-for-coupon-service
  26. 105
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/base/lightutils.go
  27. 27
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/go.mod
  28. 181
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/go.sum
  29. 9
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/main.go
  30. 131
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/rules_test.go
  31. 213
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/smoke_test.go
  32. 232
      src/loreal.com/dit/cmd/api-tests-for-coupon-service/testbase_test.go
  33. 3
      src/loreal.com/dit/cmd/ceh-cs-portal/.gitignore
  34. 5
      src/loreal.com/dit/cmd/ceh-cs-portal/.vscode/settings.json
  35. 182
      src/loreal.com/dit/cmd/ceh-cs-portal/app.go
  36. 72
      src/loreal.com/dit/cmd/ceh-cs-portal/config.default.go
  37. 83
      src/loreal.com/dit/cmd/ceh-cs-portal/config.go
  38. 48
      src/loreal.com/dit/cmd/ceh-cs-portal/db.go
  39. 149
      src/loreal.com/dit/cmd/ceh-cs-portal/endpoints.debug.go
  40. 110
      src/loreal.com/dit/cmd/ceh-cs-portal/endpoints.gateway.go
  41. 302
      src/loreal.com/dit/cmd/ceh-cs-portal/endpoints.go
  42. 218
      src/loreal.com/dit/cmd/ceh-cs-portal/logic.db.go
  43. 41
      src/loreal.com/dit/cmd/ceh-cs-portal/logic.gateway.go
  44. 150
      src/loreal.com/dit/cmd/ceh-cs-portal/logic.gateway.upstream.token.go
  45. 87
      src/loreal.com/dit/cmd/ceh-cs-portal/logic.gateway_test.go
  46. 123
      src/loreal.com/dit/cmd/ceh-cs-portal/logic.go
  47. 27
      src/loreal.com/dit/cmd/ceh-cs-portal/logic.task.maintenance.go
  48. 55
      src/loreal.com/dit/cmd/ceh-cs-portal/main.go
  49. 23
      src/loreal.com/dit/cmd/ceh-cs-portal/makefile
  50. 25
      src/loreal.com/dit/cmd/ceh-cs-portal/message.go
  51. 14
      src/loreal.com/dit/cmd/ceh-cs-portal/model.brand.go
  52. 41
      src/loreal.com/dit/cmd/ceh-cs-portal/model.const.go
  53. 13
      src/loreal.com/dit/cmd/ceh-cs-portal/model.go
  54. 32
      src/loreal.com/dit/cmd/ceh-cs-portal/net.config.go
  55. 82
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/adapter.sqlite.go
  56. 110
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/adapter.sqlite.impl.go
  57. 261
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/adapter.sqlite.sql.go
  58. 139
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/adapter.sqlite.utils.go
  59. 47
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/adapter.sqlite_test.go
  60. 262
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/endpoint.go
  61. 67
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/endpoint_test.go
  62. 3
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/go.mod
  63. 20
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/init.go
  64. 31
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/interface.go
  65. 58
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/tag.go
  66. 43
      src/loreal.com/dit/cmd/ceh-cs-portal/restful/utils.go
  67. 14
      src/loreal.com/dit/cmd/ceh-cs-portal/task.register.go
  68. 45
      src/loreal.com/dit/cmd/coupon-service/.vscode/launch.json
  69. 15
      src/loreal.com/dit/cmd/coupon-service/.vscode/settings.json
  70. 16
      src/loreal.com/dit/cmd/coupon-service/Dockerfile
  71. 191
      src/loreal.com/dit/cmd/coupon-service/app.go
  72. 34
      src/loreal.com/dit/cmd/coupon-service/base/baseerror.go
  73. 35
      src/loreal.com/dit/cmd/coupon-service/base/baseerror_test.go
  74. 69
      src/loreal.com/dit/cmd/coupon-service/base/config.default.go
  75. 84
      src/loreal.com/dit/cmd/coupon-service/base/config.go
  76. 142
      src/loreal.com/dit/cmd/coupon-service/base/lightutils.go
  77. 69
      src/loreal.com/dit/cmd/coupon-service/base/lightutils_test.go
  78. 33
      src/loreal.com/dit/cmd/coupon-service/base/requester.go
  79. 315
      src/loreal.com/dit/cmd/coupon-service/coupon/db.coupon.go
  80. 419
      src/loreal.com/dit/cmd/coupon-service/coupon/db.coupon_test.go
  81. 143
      src/loreal.com/dit/cmd/coupon-service/coupon/errors.go
  82. 41
      src/loreal.com/dit/cmd/coupon-service/coupon/logic.coupon.go
  83. 828
      src/loreal.com/dit/cmd/coupon-service/coupon/logic.couponservice.go
  84. 1502
      src/loreal.com/dit/cmd/coupon-service/coupon/logic.couponservice_test.go
  85. 304
      src/loreal.com/dit/cmd/coupon-service/coupon/logic.judge.go
  86. 432
      src/loreal.com/dit/cmd/coupon-service/coupon/logic.judge_test.go
  87. 100
      src/loreal.com/dit/cmd/coupon-service/coupon/logic.rulecomposer.go
  88. 149
      src/loreal.com/dit/cmd/coupon-service/coupon/logic.rulecomposer_test.go
  89. 21
      src/loreal.com/dit/cmd/coupon-service/coupon/message.go
  90. 150
      src/loreal.com/dit/cmd/coupon-service/coupon/module.coupon.go
  91. 46
      src/loreal.com/dit/cmd/coupon-service/coupon/module.rule.go
  92. 237
      src/loreal.com/dit/cmd/coupon-service/coupon/ruleengine.go
  93. 364
      src/loreal.com/dit/cmd/coupon-service/coupon/ruleengine_test.go
  94. 11
      src/loreal.com/dit/cmd/coupon-service/coupon/statics.go
  95. 98
      src/loreal.com/dit/cmd/coupon-service/coupon/test/coupon_types.json
  96. 48
      src/loreal.com/dit/cmd/coupon-service/coupon/test/rules.json
  97. 116
      src/loreal.com/dit/cmd/coupon-service/coupon/testbase_test.go
  98. 42
      src/loreal.com/dit/cmd/coupon-service/db.go
  99. 485
      src/loreal.com/dit/cmd/coupon-service/docs/Larry的工作内容交接.md
  100. 210
      src/loreal.com/dit/cmd/coupon-service/docs/authorization server handbook.md

9
.gitignore

@ -0,0 +1,9 @@
dit/cmd/ceh-cs-portal/__debug_bin
web/**/node_modules
dump.rdb
__debug_bin
data/
testdata.sqlite
*.sqlite
dit/cmd/coupon-service/coupon/debug.test
*.a

4
bin/build.cmd

@ -0,0 +1,4 @@
cd ..\src\loreal.com\dit\cmd\coupon-service
make windows
cd ..\..\..\..\..\bin

5
bin/build.sh

@ -0,0 +1,5 @@
#!/bin/bash
cd ../src/loreal.com/dit/cmd/coupon-service
make linux
cd ../../../../../bin

4
bin/test.cmd

@ -0,0 +1,4 @@
cd ..\src\loreal.com\dit\cmd\coupon-service
make test
cd ..\..\..\..\..\bin

5
bin/test.sh

@ -0,0 +1,5 @@
#!/bin/bash
cd ../src/loreal.com/dit/cmd/coupon-service
make test
cd ../../../../../bin

8
src/loreal.com/.gitignore

@ -0,0 +1,8 @@
dit/cmd/ceh-cs-portal/__debug_bin
web/**/node_modules
dump.rdb
__debug_bin
data/
testdata.sqlite
*.sqlite
dit/cmd/coupon-service/coupon/debug.test

24
src/loreal.com/.vscode/launch.json

@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch ces",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "C:\\Users\\larry.yu2\\go\\src\\loreal.com\\dit\\cmd\\ceh-cs-portal\\main.go"
},
{
"name": "Debug CCS",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}\\dit\\cmd\\coupon-service\\main.go"
}
]
}

10
src/loreal.com/dit/.editorconfig

@ -0,0 +1,10 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

39
src/loreal.com/dit/.gitignore

@ -0,0 +1,39 @@
################################################
############### .gitignore ##################
################################################
# Common files generated by Node, NPM, and the
# related ecosystem.
################################################
*.seed
*.log
*.out
*.pid
################################################
# Miscellaneous
#
# Common files generated by text editors,
# operating systems, file systems, etc.
################################################
*~
*#
.idea
*.db
*.exe
config/*.json
.vscode/*
.vscode\\*
.vscode\\launch.json
.vscode/launch.json
.DS_Store
out/
debug
.debug/
src/
.DS_Store
*.code-workspace
**/config/*.json

180
src/loreal.com/dit/cmd/WechatAppHost/app.go

@ -0,0 +1,180 @@
package main
import (
"database/sql"
"loreal.com/dit/module"
"loreal.com/dit/module/modules/root"
"loreal.com/dit/endpoint"
"loreal.com/dit/middlewares"
"loreal.com/dit/utils"
"loreal.com/dit/utils/task"
"log"
"net/http"
"strings"
"sync"
"github.com/robfig/cron"
)
//App - data struct for App & configuration file
type App struct {
Name string
Description string
Config *Config
Root *root.Module
Endpoints map[string]EndpointEntry
MessageHandlers map[string]func(*module.Message) bool
AuthProvider middlewares.RoleVerifier
Scheduler *cron.Cron
TaskManager *task.Manager
wg *sync.WaitGroup
mutex *sync.RWMutex
Runtime map[string]*RuntimeEnv
}
//RuntimeEnv - runtime env
type RuntimeEnv struct {
Config *Env
stmts map[string]*sql.Stmt
db *sql.DB
KVStore map[string]interface{}
mutex *sync.RWMutex
}
//Get - get value from kvstore in memory
func (rt *RuntimeEnv) Get(key string) (value interface{}, ok bool) {
rt.mutex.RLock()
defer rt.mutex.RUnlock()
value, ok = rt.KVStore[key]
return
}
//Retrive - get value from kvstore in memory, and delete it
func (rt *RuntimeEnv) Retrive(key string) (value interface{}, ok bool) {
rt.mutex.Lock()
defer rt.mutex.Unlock()
value, ok = rt.KVStore[key]
if ok {
delete(rt.KVStore, key)
}
return
}
//Set - set value to kvstore in memory
func (rt *RuntimeEnv) Set(key string, value interface{}) {
rt.mutex.Lock()
defer rt.mutex.Unlock()
rt.KVStore[key] = value
}
//EndpointEntry - endpoint registry entry
type EndpointEntry struct {
Handler func(http.ResponseWriter, *http.Request)
Middlewares []endpoint.ServerMiddleware
}
//NewApp - create new app
func NewApp(name, description string, config *Config) *App {
if config == nil {
log.Println("Missing configuration data")
return nil
}
endpoint.SetPrometheus(strings.Replace(name, "-", "_", -1))
app := &App{
Name: name,
Description: description,
Config: config,
Root: root.NewModule(name, description, config.Prefix),
Endpoints: make(map[string]EndpointEntry, 0),
MessageHandlers: make(map[string]func(*module.Message) bool, 0),
Scheduler: cron.New(),
wg: &sync.WaitGroup{},
mutex: &sync.RWMutex{},
Runtime: make(map[string]*RuntimeEnv),
}
app.TaskManager = task.NewManager(app, 100)
return app
}
//Init - app initialization
func (a *App) Init() {
if a.Config != nil {
a.Config.fixPrefix()
for _, env := range a.Config.Envs {
utils.MakeFolder(env.DataFolder)
a.Runtime[env.Name] = &RuntimeEnv{
Config: env,
KVStore: make(map[string]interface{}, 1024),
mutex: &sync.RWMutex{},
}
}
a.InitDB()
}
a.registerEndpoints()
a.registerMessageHandlers()
a.registerTasks()
// utils.LoadOrCreateJSON("./saved_status.json", &a.Status)
a.Root.OnStop = func(p *module.Module) {
a.TaskManager.SendAll("stop")
a.wg.Wait()
}
a.Root.OnDispose = func(p *module.Module) {
for _, env := range a.Runtime {
if env.db != nil {
log.Println("Close sqlite for", env.Config.Name)
env.db.Close()
}
}
// utils.SaveJSON(a.Status, "./saved_status.json")
}
}
//registerEndpoints - Register Endpoints
func (a *App) registerEndpoints() {
a.initEndpoints()
for path, entry := range a.Endpoints {
if entry.Middlewares == nil {
entry.Middlewares = a.getDefaultMiddlewares(path)
}
a.Root.MountingPoints[path] = endpoint.DecorateServer(
endpoint.Impl(entry.Handler),
entry.Middlewares...,
)
}
}
//registerMessageHandlers - Register Message Handlers
func (a *App) registerMessageHandlers() {
a.initMessageHandlers()
for path, handler := range a.MessageHandlers {
a.Root.AddMessageHandler(path, handler)
}
}
//StartScheduler - register and start the scheduled tasks
func (a *App) StartScheduler() {
if a.Scheduler == nil {
a.Scheduler = cron.New()
} else {
a.Scheduler.Stop()
a.Scheduler = cron.New()
}
for _, item := range a.Config.ScheduledTasks {
log.Println("[INFO] - Adding task:", item.Task)
func() {
s := item.Schedule
t := item.Task
a.Scheduler.AddFunc(s, func() {
a.TaskManager.RunTask(t, item.DefaultArgs...)
})
}()
}
a.Scheduler.Start()
}
//ListenAndServe - Start app
func (a *App) ListenAndServe() {
a.Init()
a.StartScheduler()
a.Root.ListenAndServe(a.Config.Address)
}

66
src/loreal.com/dit/cmd/WechatAppHost/config.default.go

@ -0,0 +1,66 @@
package main
//GORoutingNumberForWechat - Total GO Routing # for send process
const GORoutingNumberForWechat = 10
//ScheduledTask - command and parameters to run
/* Schedule Spec:
Field name | Mandatory? | Allowed values | Allowed special characters
---------- | ---------- | -------------- | --------------------------
Seconds | Yes | 0-59 | * / , -
Minutes | Yes | 0-59 | * / , -
Hours | Yes | 0-23 | * / , -
Day of month | Yes | 1-31 | * / , - ?
Month | Yes | 1-12 or JAN-DEC | * / , -
Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
Entry | Description | Equivalent To
----- | ----------- | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 *
@monthly | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0
@daily (or @midnight) | Run once a day, midnight | 0 0 0 * * *
@hourly | Run once an hour, beginning of hour | 0 0 * * * *
***
*** corn example ***:
c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.
...
// Funcs may also be added to a running Cron
c.AddFunc("@daily", func() { fmt.Println("Every day") })
..
// Inspect the cron job entries' next and previous run times.
inspect(c.Entries())
..
c.Stop() // Stop the scheduler (does not stop any jobs already running).
*/
var cfg = Config{
AppID: "myapp",
Address: ":1501",
Prefix: "/",
RedisServerStr: "localhost:6379",
Envs: []*Env{
{
Name: "prod",
SqliteDB: "prod.db",
DataFolder: "./data/",
},
{
Name: "pp",
SqliteDB: "pp.db",
DataFolder: "./data/",
},
},
ScheduledTasks: []*ScheduledTask{
{Schedule: "0 0 0 * * *", Task: "daily-maintenance", DefaultArgs: []string{}},
{Schedule: "0 10 0 * * *", Task: "daily-maintenance-pp", DefaultArgs: []string{}},
},
}

79
src/loreal.com/dit/cmd/WechatAppHost/config.go

@ -0,0 +1,79 @@
package main
import "strings"
//Config - data struct for configuration file
type Config struct {
AppID string `json:"appid"`
Address string `json:"address"`
Prefix string `json:"prefix"`
RedisServerStr string `json:"redis-server"`
AppDomainName string `json:"app-domain-name"`
TokenServiceURL string `json:"token-service-url"`
TokenServiceUsername string `json:"token-service-user"`
TokenServicePassword string `json:"token-service-password"`
Envs []*Env `json:"envs,omitempty"`
ScheduledTasks []*ScheduledTask `json:"scheduled-tasks,omitempty"`
}
func (c *Config) fixPrefix() {
if !strings.HasPrefix(c.Prefix, "/") {
c.Prefix = "/" + c.Prefix
}
if !strings.HasSuffix(c.Prefix, "/") {
c.Prefix = c.Prefix + "/"
}
}
//Env - env configuration
type Env struct {
Name string `json:"name,omitempty"`
SqliteDB string `json:"sqlite-db,omitempty"`
DataFolder string `json:"data,omitempty"`
}
//ScheduledTask - command and parameters to run
/* Schedule Spec:
Field name | Mandatory? | Allowed values | Allowed special characters
---------- | ---------- | -------------- | --------------------------
Seconds | Yes | 0-59 | * / , -
Minutes | Yes | 0-59 | * / , -
Hours | Yes | 0-23 | * / , -
Day of month | Yes | 1-31 | * / , - ?
Month | Yes | 1-12 or JAN-DEC | * / , -
Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
Entry | Description | Equivalent To
----- | ----------- | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 *
@monthly | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0
@daily (or @midnight) | Run once a day, midnight | 0 0 0 * * *
@hourly | Run once an hour, beginning of hour | 0 0 * * * *
***
*** corn example ***:
c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.
...
// Funcs may also be added to a running Cron
c.AddFunc("@daily", func() { fmt.Println("Every day") })
..
// Inspect the cron job entries' next and previous run times.
inspect(c.Entries())
..
c.Stop() // Stop the scheduler (does not stop any jobs already running).
*/
//ScheduledTask - Scheduled Task
type ScheduledTask struct {
Schedule string `json:"schedule,omitempty"`
Task string `json:"task,omitempty"`
DefaultArgs []string `json:"default-args,omitempty"`
}

49
src/loreal.com/dit/cmd/WechatAppHost/db.go

@ -0,0 +1,49 @@
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3"
)
//InitDB - initialized database
func (a *App) InitDB() {
//init database tables
sqlStmts := []string{
//PV计数
`CREATE TABLE IF NOT EXISTS visit (
openid TEXT DEFAULT '',
pageid TEXT DEFAULT '',
scene TEXT DEFAULT '',
state TEXT INTEGER DEFAULT 0,
pv INTEGER DEFAULT 0,
createat DATETIME,
recent DATETIME
);`,
"CREATE INDEX IF NOT EXISTS idx_visit_openid ON visit(openid);",
"CREATE INDEX IF NOT EXISTS idx_visit_pageid ON visit(pageid);",
"CREATE INDEX IF NOT EXISTS idx_visit_scene ON visit(scene);",
"CREATE INDEX IF NOT EXISTS idx_visit_state ON visit(state);",
"CREATE INDEX IF NOT EXISTS idx_visit_createat ON visit(createat);",
}
var err error
for _, env := range a.Runtime {
env.db, err = sql.Open("sqlite3", fmt.Sprintf("%s%s?cache=shared&mode=rwc", env.Config.DataFolder, env.Config.SqliteDB))
if err != nil {
log.Fatal(err)
}
log.Printf("[INFO] - Initialization DB for [%s]...\n", env.Config.Name)
for _, sqlStmt := range sqlStmts {
_, err := env.db.Exec(sqlStmt)
if err != nil {
log.Printf("[ERR] - [InitDB] %q: %s\n", err, sqlStmt)
return
}
}
env.stmts = make(map[string]*sql.Stmt, 0)
log.Printf("[INFO] - DB for [%s] ready!\n", env.Config.Name)
}
}

73
src/loreal.com/dit/cmd/WechatAppHost/endpoints.entry.go

@ -0,0 +1,73 @@
package main
import (
"encoding/json"
"loreal.com/dit/utils"
"log"
"net/http"
)
/* 以下为具体 Endpoint 实现代码 */
//entryHandler - entry point for frontend web pages, to get initData in cookie
//endpoint: entry.html
//method: GET
func (a *App) entryHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
showError(w, r, "", "无效的方法")
return
}
q := r.URL.Query()
// var userid int64
// userid = -1
appid := sanitizePolicy.Sanitize(q.Get("appid"))
env := a.getEnv(appid)
rt := a.getRuntime(env)
// states := parseState(sanitizePolicy.Sanitize(q.Get("state")))
scene := sanitizePolicy.Sanitize(q.Get("state"))
_ = sanitizePolicy.Sanitize(q.Get("token"))
openid := sanitizePolicy.Sanitize(q.Get("openid"))
dataObject := map[string]interface{}{
"appid": appid,
"scene": scene,
"openid": openid,
}
// follower, nickname := a.wxUserKeyInfo(openid)
// dataObject["nickname"] = nickname
if rt != nil {
// if err := a.recordUser(
// rt,
// openid,
// scene,
// "0",
// &userid,
// ); err != nil {
// log.Println("[ERR] - [EP][entry.html], err:", err)
// }
}
if q.Get("debug") == "1" {
w.Header().Set("Content-Type", "application/json;charset=utf-8")
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
if err := encoder.Encode(dataObject); err != nil {
log.Println("[ERR] - JSON encode error:", err)
http.Error(w, "500", http.StatusInternalServerError)
}
return
}
w.Header().Set("Content-Type", "text/html;charset=utf-8")
// cookieValue := url.PathEscape(utils.MarshalJSON(dataObject))
if DEBUG {
log.Println("[DEBUG] - set-cookie:", utils.MarshalJSON(dataObject))
}
// http.SetCookie(w, &http.Cookie{
// Name: "initdata",
// Value: cookieValue,
// HttpOnly: false,
// Secure: false,
// MaxAge: 0,
// })
http.ServeFile(w, r, "./public/index.html")
}

223
src/loreal.com/dit/cmd/WechatAppHost/endpoints.go

@ -0,0 +1,223 @@
package main
import (
"loreal.com/dit/endpoint"
"loreal.com/dit/loreal/webservice"
"loreal.com/dit/middlewares"
"html/template"
"log"
"net/http"
"strconv"
"github.com/microcosm-cc/bluemonday"
)
// var seededRand *rand.Rand
var sanitizePolicy *bluemonday.Policy
var errorTemplate *template.Template
func init() {
// seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
sanitizePolicy = bluemonday.UGCPolicy()
var err error
errorTemplate, _ = template.ParseFiles("./template/error.tpl")
if err != nil {
log.Panic("[ERR] - Parsing error template", err)
}
}
func (a *App) initEndpoints() {
a.Endpoints = map[string]EndpointEntry{
"entry.html": {Handler: a.entryHandler, Middlewares: a.noAuthMiddlewares("entry.html")},
"api/kvstore": {Handler: a.kvstoreHandler, Middlewares: a.noAuthMiddlewares("api/kvstore")},
"api/visit": {Handler: a.pvHandler, Middlewares: a.noAuthMiddlewares("api/visit")},
"error": {Handler: a.errorHandler, Middlewares: a.noAuthMiddlewares("error")},
"report/visit": {Handler: a.reportVisitHandler},
}
}
//noAuthMiddlewares - middlewares without auth
func (a *App) noAuthMiddlewares(path string) []endpoint.ServerMiddleware {
return []endpoint.ServerMiddleware{
middlewares.NoCache(),
middlewares.ServerInstrumentation(path, endpoint.RequestCounter, endpoint.LatencyHistogram, endpoint.DurationsSummary),
}
}
//tokenAuthMiddlewares - middlewares auth by token
// func (a *App) tokenAuthMiddlewares(path string) []endpoint.ServerMiddleware {
// return []endpoint.ServerMiddleware{
// middlewares.NoCache(),
// middlewares.ServerInstrumentation(path, endpoint.RequestCounter, endpoint.LatencyHistogram, endpoint.DurationsSummary),
// a.signatureVerifier(),
// }
// }
//getDefaultMiddlewares - middlewares installed by defaults
func (a *App) getDefaultMiddlewares(path string) []endpoint.ServerMiddleware {
return []endpoint.ServerMiddleware{
middlewares.NoCache(),
middlewares.BasicAuthOrTokenAuthWithRole(a.AuthProvider, "", "user,admin"),
middlewares.ServerInstrumentation(
path,
endpoint.RequestCounter,
endpoint.LatencyHistogram,
endpoint.DurationsSummary,
),
}
}
func (a *App) getEnv(appid string) string {
if appid == a.Config.AppID {
return "prod"
}
return "pp"
}
/* 以下为具体 Endpoint 实现代码 */
//errorHandler - query error info
//endpoint: error
//method: GET
func (a *App) errorHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "Not Acceptable", http.StatusNotAcceptable)
return
}
q := r.URL.Query()
title := sanitizePolicy.Sanitize(q.Get("title"))
errmsg := sanitizePolicy.Sanitize(q.Get("errmsg"))
if err := errorTemplate.Execute(w, map[string]interface{}{
"title": title,
"errmsg": errmsg,
}); err != nil {
log.Println("[ERR] - errorTemplate error:", err)
http.Error(w, "500", http.StatusInternalServerError)
}
}
/* 以下为具体 Endpoint 实现代码 */
//kvstoreHandler - get value from kvstore in runtime
//endpoint: /api/kvstore
//method: GET
func (a *App) kvstoreHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
outputJSON(w, webservice.APIStatus{
ErrCode: -100,
ErrMessage: "Method not acceptable",
})
return
}
q := r.URL.Query()
ticket := q.Get("ticket")
env := a.getEnv(q.Get("appid"))
rt := a.getRuntime(env)
if rt == nil {
outputJSON(w, webservice.APIStatus{
ErrCode: -1,
ErrMessage: "invalid appid",
})
return
}
var result struct {
Value interface{} `json:"value"`
}
var ok bool
var v interface{}
v, ok = rt.Retrive(ticket)
if !ok {
outputJSON(w, webservice.APIStatus{
ErrCode: -2,
ErrMessage: "invalid ticket",
})
return
}
switch val := v.(type) {
case chan interface{}:
// log.Println("[Hu Bin] - Get Value Chan:", val)
result.Value = <-val
// log.Println("[Hu Bin] - Get Value from Chan:", result.Value)
default:
// log.Println("[Hu Bin] - Get Value:", val)
result.Value = val
}
outputJSON(w, result)
}
//pvHandler - record PV/UV
//endpoint: /api/visit
//method: GET
func (a *App) pvHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
outputJSON(w, map[string]interface{}{
"code": -1,
"msg": "Not support",
})
return
}
q := r.URL.Query()
appid := q.Get("appid")
env := a.getEnv(appid)
rt := a.getRuntime(env)
if rt == nil {
log.Println("[ERR] - Invalid appid:", appid)
outputJSON(w, map[string]interface{}{
"code": -2,
"msg": "Invalid APPID",
})
return
}
openid := sanitizePolicy.Sanitize(q.Get("openid"))
pageid := sanitizePolicy.Sanitize(q.Get("pageid"))
scene := sanitizePolicy.Sanitize(q.Get("scene"))
visitState, _ := strconv.Atoi(sanitizePolicy.Sanitize(q.Get("type")))
if err := a.recordPV(
rt,
openid,
pageid,
scene,
visitState,
); err != nil {
log.Println("[ERR] - [EP][api/visit], err:", err)
outputJSON(w, map[string]interface{}{
"code": -3,
"msg": "internal error",
})
return
}
outputJSON(w, map[string]interface{}{
"code": 0,
"msg": "ok",
})
}
//CSV BOM
//file.Write([]byte{0xef, 0xbb, 0xbf})
func outputExcel(w http.ResponseWriter, b []byte, filename string) {
w.Header().Add("Content-Disposition", "attachment; filename="+filename)
//w.Header().Add("Content-Type", "application/vnd.ms-excel")
w.Header().Add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
// w.Header().Add("Content-Transfer-Encoding", "binary")
w.Write(b)
}
func outputText(w http.ResponseWriter, b []byte) {
w.Header().Add("Content-Type", "text/plain;charset=utf-8")
w.Write(b)
}
func showError(w http.ResponseWriter, r *http.Request, title, message string) {
if err := errorTemplate.Execute(w, map[string]interface{}{
"title": title,
"errmsg": message,
}); err != nil {
log.Println("[ERR] - errorTemplate error:", err)
http.Error(w, "500", http.StatusInternalServerError)
}
}

51
src/loreal.com/dit/cmd/WechatAppHost/endpoints.report.go

@ -0,0 +1,51 @@
package main
import (
"fmt"
"log"
"net/http"
"time"
)
//reportVisitHandler - export visit detail report in excel format
//endpoint: /report/visit
//method: GET
func (a *App) reportVisitHandler(w http.ResponseWriter, r *http.Request) {
const dateFormat = "2006-01-02"
if r.Method != "GET" {
showError(w, r, "明细报表下载", "调用方法不正确")
return
}
q := r.URL.Query()
env := a.getEnv(q.Get("appid"))
rt := a.getRuntime(env)
if rt == nil {
showError(w, r, "明细报表下载", "参数错误, APPID不正确")
return
}
var err error
from, err := time.ParseInLocation(dateFormat, sanitizePolicy.Sanitize(q.Get("from")), time.Local)
if err != nil {
showError(w, r, "明细报表下载", "参数错误, 开始时间‘from’格式不正确")
return
}
to, err := time.ParseInLocation(dateFormat, sanitizePolicy.Sanitize(q.Get("to")), time.Local)
if err != nil {
showError(w, r, "明细报表下载", "参数错误, 结束时间‘to’格式不正确")
return
}
to = to.Add(time.Second*(60*60*24-1) + time.Millisecond*999) //23:59:59.999
xlsxFile, err := a.genVisitReportXlsx(rt, from, to)
if err != nil {
log.Println("[ERR] - [ep][report/download], err:", err)
showError(w, r, "明细报表下载", "查询报表时发生错误, 请联系管理员查看日志。")
return
}
fileName := fmt.Sprintf("visit-report-%s-%s.xlsx", from.Format("0102"), to.Format("0102"))
w.Header().Add("Content-Disposition", "attachment; filename="+fileName)
w.Header().Add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
if err := xlsxFile.Write(w); err != nil {
showError(w, r, "明细报表下载", "查询报表时发生错误, 无法生存Xlsx文件。")
return
}
}

123
src/loreal.com/dit/cmd/WechatAppHost/logic.db.go

@ -0,0 +1,123 @@
package main
func (a *App) recordPV(
runtime *RuntimeEnv,
openid, pageid, scene string,
visitState int,
) (err error) {
const stmtNameNewPV = "insert-visit"
const stmtSQLNewPV = "INSERT INTO visit (openid,pageid,scene,createAt,recent,pv,state) VALUES (?,?,?,datetime('now','localtime'),datetime('now','localtime'),1,?);"
const stmtNamePV = "update-pv"
const stmtSQLPV = "UPDATE visit SET pv=pv+1,recent=datetime('now','localtime') WHERE openid=? AND pageid=? AND scene=? AND state=?;"
stmtPV := a.getStmt(runtime, stmtNamePV)
if stmtPV == nil {
if stmtPV, err = a.setStmt(runtime, stmtNamePV, stmtSQLPV); err != nil {
return
}
}
stmtNewPV := a.getStmt(runtime, stmtNameNewPV)
if stmtNewPV == nil {
if stmtNewPV, err = a.setStmt(runtime, stmtNameNewPV, stmtSQLNewPV); err != nil {
return
}
}
runtime.mutex.Lock()
defer runtime.mutex.Unlock()
tx, err := runtime.db.Begin()
if err != nil {
return err
}
stmtPV = tx.Stmt(stmtPV)
pvResult, err := stmtPV.Exec(
openid,
pageid,
scene,
visitState,
)
if err != nil {
tx.Rollback()
return err
}
cnt, err := pvResult.RowsAffected()
if err != nil {
tx.Rollback()
return err
}
if cnt > 0 {
tx.Commit()
return
}
stmtNewPV = tx.Stmt(stmtNewPV)
_, err = stmtNewPV.Exec(
openid,
pageid,
scene,
visitState,
)
if err != nil {
tx.Rollback()
return err
}
tx.Commit()
return
}
// func (a *App) recordQRScan(
// openid string,
// ) (err error) {
// for _, env := range a.Config.Envs {
// rt := a.getRuntime(env.Name)
// if rt == nil {
// continue
// }
// a.doRecordQRScan(rt, openid)
// }
// return nil
// }
// func (a *App) doRecordQRScan(
// runtime *RuntimeEnv,
// openid string,
// ) (err error) {
// const stmtNameAdd = "add-openid"
// const stmtSQLAdd = "INSERT INTO qrscan (openid,createat) VALUES (?,datetime('now','localtime'));"
// const stmtNameRecord = "record-scan"
// const stmtSQLRecord = "UPDATE qrscan SET scanCnt=scanCnt+1,recent=datetime('now','localtime') WHERE openid=?;"
// stmtAdd := a.getStmt(runtime, stmtNameAdd)
// if stmtAdd == nil {
// //lazy setup for stmt
// if stmtAdd, err = a.setStmt(runtime, stmtNameAdd, stmtSQLAdd); err != nil {
// return
// }
// }
// stmtRecord := a.getStmt(runtime, stmtSQLRecord)
// if stmtRecord == nil {
// if stmtRecord, err = a.setStmt(runtime, stmtNameRecord, stmtSQLRecord); err != nil {
// return
// }
// }
// runtime.mutex.Lock()
// defer runtime.mutex.Unlock()
// tx, err := runtime.db.Begin()
// if err != nil {
// return err
// }
// //Add scan
// stmtAdd = tx.Stmt(stmtAdd)
// _, err = stmtAdd.Exec(
// openid,
// )
// if err != nil && !strings.HasPrefix(err.Error(), "UNIQUE") {
// tx.Rollback()
// return err
// }
// //record scan
// stmtRecord = tx.Stmt(stmtRecord)
// _, err = stmtRecord.Exec(openid)
// if err != nil {
// tx.Rollback()
// return err
// }
// tx.Commit()
// return
// }

188
src/loreal.com/dit/cmd/WechatAppHost/logic.go

@ -0,0 +1,188 @@
package main
import (
"database/sql"
"encoding/json"
"loreal.com/dit/module/modules/loreal"
"loreal.com/dit/wechat"
"fmt"
"log"
"net/http"
"net/url"
"os"
"regexp"
"strconv"
"strings"
)
//DEBUG - whether in debug mode
var DEBUG bool
//INFOLEVEL - info level for debug mode
var INFOLEVEL int
//LOGLEVEL - info level for logs
var LOGLEVEL int
var wxAccount *wechat.Account
func init() {
wxAccount = wechat.Accounts["default"]
if wxAccount.Token.Requester == nil {
wxAccount.Token.Requester = loreal.NewWechatTokenService(wxAccount)
}
if os.Getenv("EV_DEBUG") != "" {
DEBUG = true
}
INFOLEVEL = 1
LOGLEVEL = 1
}
//var wxAccount = wechat.Accounts["default"]
func lorealCardValid(cardNo string) bool {
if len(cardNo) != 22 {
return false
}
re := regexp.MustCompile("\\d{22}")
if !re.MatchString(cardNo) {
return false
}
return (cardNo == lorealCardCheckSum(cardNo[:20]))
}
//lorealCardCheckSum calculate 2 check sum bit for loreal card
func lorealCardCheckSum(cardNo string) string {
firstLineWeight := []int{1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2}
secondLineWeight := []int{4, 3, 2, 7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2}
cardRunes := []rune(cardNo)
var firstDigital, secondDigital int
var temp, result int
for i := 0; i <= 19; i++ {
cardBit, _ := strconv.Atoi(string(cardRunes[i]))
temp = cardBit * firstLineWeight[i]
if temp > 9 {
temp -= 9
}
result += temp
}
firstDigital = result % 10
if firstDigital != 0 {
firstDigital = 10 - firstDigital
}
cardNo = fmt.Sprintf("%s%d", cardNo, firstDigital)
cardRunes = []rune(cardNo)
result = 0
for i := 0; i <= 20; i++ {
cardBit, _ := strconv.Atoi(string(cardRunes[i]))
temp = cardBit * secondLineWeight[i]
result += temp
}
secondDigital = 11 - result%11
if secondDigital > 9 {
secondDigital = 0
}
return fmt.Sprintf("%s%d", cardNo, secondDigital)
}
func retry(count int, fn func() error) error {
total := count
retry:
err := fn()
if err != nil {
count--
log.Println("[INFO] - Retry: ", total-count)
if count > 0 {
goto retry
}
}
return err
}
func parseState(state string) map[string]string {
result := make(map[string]string, 2)
var err error
state, err = url.PathUnescape(state)
if err != nil {
log.Println("[ERR] - parseState", err)
return result
}
if DEBUG {
log.Println("[DEBUG] - PathUnescape state:", state)
}
states := strings.Split(state, ";")
for _, kv := range states {
sp := strings.Index(kv, ":")
if sp < 0 {
//empty value
result[kv] = ""
continue
}
result[kv[:sp]] = kv[sp+1:]
}
return result
}
func (a *App) getRuntime(env string) *RuntimeEnv {
runtime, ok := a.Runtime[env]
if !ok {
return nil
}
return runtime
}
//getStmt - get stmt from app safely
func (a *App) getStmt(runtime *RuntimeEnv, name string) *sql.Stmt {
runtime.mutex.RLock()
defer runtime.mutex.RUnlock()
if stmt, ok := runtime.stmts[name]; ok {
return stmt
}
return nil
}
//getStmt - get stmt from app safely
func (a *App) setStmt(runtime *RuntimeEnv, name, query string) (stmt *sql.Stmt, err error) {
stmt, err = runtime.db.Prepare(query)
if err != nil {
logError(err, name)
return nil, err
}
runtime.mutex.Lock()
runtime.stmts[name] = stmt
runtime.mutex.Unlock()
return stmt, nil
}
//outputJSON - output json for http response
func outputJSON(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json;charset=utf-8")
enc := json.NewEncoder(w)
if DEBUG {
enc.SetIndent("", " ")
}
if err := enc.Encode(data); err != nil {
log.Println("[ERR] - JSON encode error:", err)
http.Error(w, "500", http.StatusInternalServerError)
return
}
}
func logError(err error, msg string) {
if err != nil {
log.Printf("[ERR] - %s, err: %v\n", msg, err)
}
}
func debugInfo(source, msg string, level int) {
if DEBUG && INFOLEVEL >= level {
log.Printf("[DEBUG] - [%s]%s\n", source, msg)
}
}

86
src/loreal.com/dit/cmd/WechatAppHost/logic.report.go

@ -0,0 +1,86 @@
package main
import (
"fmt"
"time"
"github.com/tealeg/xlsx"
)
/*
openid TEXT DEFAULT '',
pageid TEXT DEFAULT '',
scene TEXT DEFAULT '',
state TEXT INTEGER DEFAULT 0,
pv INTEGER DEFAULT 0,
createat DATETIME,
recent DATETIME
*/
type visitRecord struct {
OpenID string
PageID string
Scene string
PV int
CreateAt time.Time
Recent time.Time
}
func (a *App) genVisitReportXlsx(
runtime *RuntimeEnv,
from time.Time,
to time.Time,
) (f *xlsx.File, err error) {
const stmtName = "visit-report"
const stmtSQL = "SELECT openid,pageid,scene,pv,createat,recent FROM visit WHERE createat>=? AND createat<=?;"
stmt := a.getStmt(runtime, stmtName)
if stmt == nil {
//lazy setup for stmt
if stmt, err = a.setStmt(runtime, stmtName, stmtSQL); err != nil {
return
}
}
runtime.mutex.Lock()
defer runtime.mutex.Unlock()
rows, err := stmt.Query(from, to)
if err != nil {
return
}
var headerRow *xlsx.Row
f = xlsx.NewFile()
sheet, err := f.AddSheet(fmt.Sprintf("PV details %s-%s", from.Format("0102"), to.Format("0102")))
if err != nil {
return nil, err
}
headerRow = sheet.AddRow()
headerRow.AddCell().SetString("OpenID")
headerRow.AddCell().SetString("页面号")
headerRow.AddCell().SetString("入口场景")
headerRow.AddCell().SetString("PV")
headerRow.AddCell().SetString("首次进入时间")
headerRow.AddCell().SetString("最后进入时间")
for rows.Next() {
r := sheet.AddRow()
vr := visitRecord{}
if err = rows.Scan(
&vr.OpenID,
&vr.PageID,
&vr.Scene,
&vr.PV,
&vr.CreateAt,
&vr.Recent,
); err != nil {
return
}
r.AddCell().SetString(vr.OpenID)
r.AddCell().SetString(vr.PageID)
r.AddCell().SetString(vr.Scene)
r.AddCell().SetInt(vr.PV)
r.AddCell().SetDateTime(vr.CreateAt)
r.AddCell().SetDateTime(vr.Recent)
}
return f, nil
}

27
src/loreal.com/dit/cmd/WechatAppHost/logic.task.maintenance.go

@ -0,0 +1,27 @@
package main
import (
"loreal.com/dit/utils/task"
)
//DailyMaintenance - task to do daily maintenance
func (a *App) DailyMaintenance(t *task.Task) (err error) {
// const stmtName = "dm-clean-vehicle"
// const stmtSQL = "DELETE FROM vehicle_left WHERE enter<=?;"
// env := getEnv(t.Context)
// runtime := a.getRuntime(env)
// if runtime == nil {
// return ErrMissingRuntime
// }
// stmt := a.getStmt(runtime, stmtName)
// if stmt == nil {
// //lazy setup for stmt
// if stmt, err = a.setStmt(runtime, stmtName, stmtSQL); err != nil {
// return err
// }
// }
// runtime.mutex.Lock()
// defer runtime.mutex.Unlock()
// _, err = stmt.Exec(int(time.Now().Add(time.Hour * -168).Unix())) /* 7*24Hours = 168*/
return nil
}

56
src/loreal.com/dit/cmd/WechatAppHost/main.go

@ -0,0 +1,56 @@
//General Wechat WebAPP Host
package main
import (
"flag"
"log"
"math/rand"
"time"
"loreal.com/dit/module/modules/account"
"loreal.com/dit/module/modules/wechat"
"loreal.com/dit/utils"
)
//Version - generate on build time by makefile
var Version = "v0.1"
//CommitID - generate on build time by makefile
var CommitID = ""
func main() {
rand.Seed(time.Now().UnixNano())
const serviceName = "wxAppHost"
const serviceDescription = "Wechat WebAPP Host"
log.Println("[INFO] -", serviceName, Version+"-"+CommitID)
log.Println("[INFO] -", serviceDescription)
utils.LoadOrCreateJSON("./config/config.json", &cfg) //cfg initialized in config.go
flag.StringVar(&cfg.Address, "addr", cfg.Address, "host:port of the service")
flag.StringVar(&cfg.Prefix, "prefix", cfg.Prefix, "/path/ prefixe to service")
flag.StringVar(&cfg.RedisServerStr, "redis", cfg.RedisServerStr, "Redis connection string")
flag.StringVar(&cfg.AppDomainName, "app-domain", cfg.AppDomainName, "app domain name")
flag.Parse()
//Create Main service
var app = NewApp(serviceName, serviceDescription, &cfg)
uas := account.NewModule("account",
app.Config.RedisServerStr, /*Redis server address*/
3, /*Numbe of faild logins to lock the account */
60*time.Second, /*How long the account will stay locked*/
7200*time.Second, /*How long the token will be valid*/
)
app.Root.Install(
uas,
wechat.NewModuleWithCEHTokenService(
"wx",
app.Config.AppDomainName,
app.Config.TokenServiceURL,
app.Config.TokenServiceUsername,
app.Config.TokenServicePassword,
),
)
app.AuthProvider = uas
app.ListenAndServe()
}

25
src/loreal.com/dit/cmd/WechatAppHost/message.go

@ -0,0 +1,25 @@
package main
import (
"log"
"loreal.com/dit/module"
"loreal.com/dit/utils"
)
func (a *App) initMessageHandlers() {
a.MessageHandlers = map[string]func(*module.Message) bool{
"reload": a.reloadMessageHandler,
}
}
//reloadMessageHandler - handle reload message
func (a *App) reloadMessageHandler(msgPtr *module.Message) (handled bool) {
//reload configuration
utils.LoadOrCreateJSON("./config/config.json", &a.Config)
a.Config.fixPrefix()
a.StartScheduler()
log.Println("[INFO] - Configuration reloaded!")
return true
}

14
src/loreal.com/dit/cmd/WechatAppHost/task.register.go

@ -0,0 +1,14 @@
package main
import "loreal.com/dit/utils/task"
func (a *App) registerTasks() {
a.TaskManager.RegisterWithContext("daily-maintenance-pp", "xmillion-test", a.dailyMaintenanceTaskHandler, 1)
a.TaskManager.RegisterWithContext("daily-maintenance", "xmillion", a.dailyMaintenanceTaskHandler, 1)
}
//dailyMaintenanceTaskHandler - run daily maintenance task
func (a *App) dailyMaintenanceTaskHandler(t *task.Task, args ...string) {
//a.DailyMaintenance(t, task.GetArgs(args, 0))
a.DailyMaintenance(t)
}

16
src/loreal.com/dit/cmd/api-tests-for-coupon-service/.vscode/launch.json

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "api test",
"type": "go",
"request": "launch",
"mode": "debug",
// "backend": "native",
"program": "${workspaceFolder}/main.go"
}
]
}

BIN
src/loreal.com/dit/cmd/api-tests-for-coupon-service/api-tests-for-coupon-service

Binary file not shown.

105
src/loreal.com/dit/cmd/api-tests-for-coupon-service/base/lightutils.go

@ -0,0 +1,105 @@
package base
import (
// "bytes"
"encoding/json"
// "errors"
"fmt"
"log"
"math/rand"
// "mime/multipart"
"net/http"
"strings"
"time"
"github.com/google/uuid"
)
var r *rand.Rand = rand.New(rand.NewSource(time.Now().Unix()))
// IsBlankString 判断是否为空的ID,ID不能都是空白.
func IsBlankString(str string) bool {
return len(strings.TrimSpace(str)) == 0
}
// IsEmptyString 判断是否为空的字符串.
func IsEmptyString(str string) bool {
return len(str) == 0
}
// IsValidUUID
func IsValidUUID(u string) bool {
_, err := uuid.Parse(u)
return err == nil
}
// SetResponseHeader 一个快捷设置status code 和content type的方法
func SetResponseHeader(w http.ResponseWriter, statusCode int, contentType string) {
w.Header().Set("Content-Type", contentType)
w.WriteHeader(statusCode)
}
// WriteErrorResponse 一个快捷设置包含错误body的response
func WriteErrorResponse(w http.ResponseWriter, statusCode int, contentType string, a interface{}) {
SetResponseHeader(w, statusCode, contentType)
switch vv := a.(type) {
case error:
{
fmt.Fprintf(w, vv.Error())
}
case map[string][]error:
{
jsonBytes, err := json.Marshal(vv)
if nil != err {
log.Println(err)
fmt.Fprintf(w, err.Error())
}
var str = string(jsonBytes)
fmt.Fprintf(w, str)
}
case []error:
{
jsonBytes, err := json.Marshal(vv)
if nil != err {
log.Println(err)
fmt.Fprintf(w, err.Error())
}
var str = string(jsonBytes)
fmt.Fprintf(w, str)
}
}
}
// trimURIPrefix 将一个uri拆分为若干node,根据ndoe取得一些动态参数。
func TrimURIPrefix(uri string, stopTag string) []string {
params := strings.Split(strings.TrimPrefix(strings.TrimSuffix(uri, "/"), "/"), "/")
last := len(params) - 1
for i := last; i >= 0; i-- {
if params[i] == stopTag {
return params[i+1:]
}
}
return params
}
//outputJSON - output json for http response
func outputJSON(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json;charset=utf-8")
enc := json.NewEncoder(w)
if err := enc.Encode(data); err != nil {
log.Println("[ERR] - [outputJSON] JSON encode error:", err)
http.Error(w, "500", http.StatusInternalServerError)
return
}
}
// RandString 生成随机字符串
func RandString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
b := r.Intn(26) + 65
bytes[i] = byte(b)
}
return string(bytes)
}

27
src/loreal.com/dit/cmd/api-tests-for-coupon-service/go.mod

@ -0,0 +1,27 @@
module api-tests-for-coupon-service
go 1.13
require (
github.com/ajg/form v1.5.1 // indirect
github.com/chenhg5/collection v0.0.0-20191118032303-cb21bccce4c3
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/gavv/httpexpect v2.0.0+incompatible
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/uuid v1.1.1
github.com/gorilla/websocket v1.4.1 // indirect
github.com/imkira/go-interpol v1.1.0 // indirect
github.com/jinzhu/now v1.1.1
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/moul/http2curl v1.0.0 // indirect
github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3