瀏覽代碼

first commit

zjxpcyc 6 年之前
當前提交
2f3d07046f

+ 2
- 0
.gitignore 查看文件

@@ -0,0 +1,2 @@
1
+*.exe
2
+*.zip

+ 61
- 0
conf/app.conf 查看文件

@@ -0,0 +1,61 @@
1
+appname = voting
2
+httpport = 8080
3
+runmode = dev
4
+autorender = false
5
+copyrequestbody = true
6
+EnableDocs = true
7
+
8
+
9
+[wechat]
10
+appid = 
11
+secret = 
12
+token =
13
+aeskey = 
14
+wxid =
15
+
16
+[alioss]
17
+Endpoint=oss-cn-shanghai.aliyuncs.com
18
+AccessKeyId=LTAIkc75dpkJw8Lb
19
+AccessKeySecret=v4bvXCaix6vSDTCFfwSAdqV53iFEQw
20
+Bucket=jingcheng-h5temp
21
+
22
+[cros]
23
+allowMode = dev
24
+allowMethods = *
25
+allowOrigin = http://localhost:9528
26
+allowCredentials = true
27
+
28
+
29
+[db]
30
+; 数据库类型,目前只支持mysql
31
+db_type      = mysql
32
+
33
+; 连接协议
34
+con_protocol = tcp
35
+
36
+; 数据库地址,可以使用IP
37
+db_addr = localhost
38
+# db_addr      = rm-uf6z3z6jq11x653d77o.mysql.rds.aliyuncs.com
39
+
40
+; 端口
41
+db_port      = 3306
42
+
43
+; 用户名
44
+username     = spaceofcheng
45
+
46
+; 密码
47
+password     = spaceofcheng
48
+# password     = c;a|vK)Xv/=L@c[6Fx,v[ITD5;WpR}+Y
49
+
50
+; 数据库名或者schema
51
+database     = voting
52
+
53
+; 前缀,目前尚未使用
54
+dbprefix     = 
55
+
56
+; 模式,目前尚未使用
57
+db_debug     = false
58
+
59
+; 字符集
60
+char_set     = utf8mb4
61
+

+ 111
- 0
controllers/base.go 查看文件

@@ -0,0 +1,111 @@
1
+package controllers
2
+
3
+import (
4
+	"encoding/json"
5
+	"net/http"
6
+	"spaceofcheng/services/utils"
7
+
8
+	"github.com/astaxie/beego"
9
+)
10
+
11
+type ControllerInterface interface {
12
+	Constructor()
13
+}
14
+
15
+//BaseController 基础controller
16
+type BaseController struct {
17
+	beego.Controller
18
+}
19
+
20
+//Prepare 继承beego的
21
+func (c *BaseController) Prepare() {
22
+	c.initController()
23
+}
24
+
25
+// ResponseJSON 返回JSON数据
26
+func (c *BaseController) ResponseJSON(data interface{}) {
27
+	c.ResponseData(data, "", http.StatusOK)
28
+}
29
+
30
+// ResponseError 返回错误
31
+func (c *BaseController) ResponseError(err error, code ...int) {
32
+	if len(code) > 0 {
33
+		c.ResponseData(nil, err, code[0])
34
+	}
35
+
36
+	c.ResponseData(nil, err, http.StatusBadRequest)
37
+}
38
+
39
+// ResponseRaw 返回
40
+func (c *BaseController) ResponseRaw(msg []byte, stop ...bool) {
41
+	c.Ctx.ResponseWriter.Write(msg)
42
+
43
+	if stop != nil && len(stop) > 0 {
44
+		if !stop[0] {
45
+			return
46
+		}
47
+	}
48
+
49
+	c.StopRun()
50
+}
51
+
52
+// ResponseData 自定义的JSON返回
53
+func (c *BaseController) ResponseData(data interface{}, msg interface{}, code int) {
54
+	status := code
55
+
56
+	sendMessage := ""
57
+	switch msgVal := msg.(type) {
58
+	case error:
59
+		sendMessage = msgVal.Error()
60
+	case string:
61
+		sendMessage = msgVal
62
+	default:
63
+		sendMessage = ""
64
+	}
65
+
66
+	c.Data["json"] = map[string]interface{}{
67
+		"code":    status,
68
+		"message": sendMessage,
69
+		"result":  data,
70
+	}
71
+
72
+	c.Ctx.Output.Header("Access-Control-Expose-Headers", utils.TokenHeader)
73
+
74
+	c.crosPolicy()
75
+	c.ServeJSON()
76
+	c.StopRun()
77
+}
78
+
79
+// initAppController 执行当前路由请求对应的 Controller 初始化
80
+func (c *BaseController) initController() {
81
+	if ctrl, ok := c.AppController.(ControllerInterface); ok {
82
+		ctrl.Constructor()
83
+	}
84
+}
85
+
86
+func (c *BaseController) GetBodyData() (map[string]interface{}, error) {
87
+	var data map[string]interface{}
88
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &data)
89
+	return data, err
90
+}
91
+
92
+// Options 解决跨域先发送 options
93
+func (c *BaseController) Options() {
94
+	c.ResponseJSON("")
95
+}
96
+
97
+// crosPolicy 跨域策略
98
+func (c *BaseController) crosPolicy() {
99
+	runmode := beego.AppConfig.String("runmode")
100
+	allowMode := beego.AppConfig.String("cros::allowMode")
101
+
102
+	if runmode == allowMode {
103
+		allowMethods := beego.AppConfig.String("cros::allowMethods")
104
+		allowOrigin := beego.AppConfig.String("cros::allowOrigin")
105
+		allowCredentials := beego.AppConfig.String("cros::allowCredentials")
106
+
107
+		c.Ctx.Output.Header("Access-Control-Allow-Origin", allowOrigin)
108
+		c.Ctx.Output.Header("Access-Control-Allow-Methods", allowMethods)
109
+		c.Ctx.Output.Header("Access-Control-Allow-Credentials", allowCredentials)
110
+	}
111
+}

+ 31
- 0
controllers/object.go 查看文件

@@ -0,0 +1,31 @@
1
+package controllers
2
+
3
+import (
4
+	"strconv"
5
+	"time"
6
+	"voting/utils"
7
+)
8
+
9
+// Operations about object
10
+type ObjectController struct {
11
+	BaseController
12
+}
13
+
14
+// @Title Create
15
+// @Description create object
16
+// @Param	body		body 	models.Object	true		"The object content"
17
+// @Success 200 {string} object URL
18
+// @Failure 403 body is empty
19
+// @router / [post]
20
+func (c *ObjectController) Post() {
21
+	fNameExtra := strconv.FormatInt(time.Now().Unix(), 10)
22
+
23
+	fileURL, err := utils.UploadFileToBucket(c.Ctx.Request, "file", fNameExtra)
24
+	if err != nil {
25
+		c.ResponseError(err)
26
+	}
27
+
28
+	c.ResponseJSON(map[string]interface{}{
29
+		"url": fileURL,
30
+	})
31
+}

+ 59
- 0
controllers/voting.go 查看文件

@@ -0,0 +1,59 @@
1
+package controllers
2
+
3
+import (
4
+	"errors"
5
+	"strconv"
6
+	"voting/models"
7
+)
8
+
9
+// VotingController 是投票 controller
10
+type VotingController struct {
11
+	BaseController
12
+	db *models.VotingModel
13
+}
14
+
15
+// Constructor 初始化
16
+func (c *VotingController) Constructor() {
17
+	c.db = new(models.VotingModel)
18
+}
19
+
20
+// @Title List
21
+// @Description 获取报名人员列表
22
+// @Param   actid   path   string  true	"活动ID"
23
+// @Param   page   query   int  false	"分页"
24
+// @Param   q   query   string  false	"查询内容, 不填即为查询全部"
25
+// @Param   tab   query   int  false	"列表类型, 1 为最新上传, 2 为 Top50"
26
+// @Success 200 [object] models.MemInfo
27
+// @Failure 403 message of error
28
+// @router /members [get]
29
+func (c *VotingController) List() {
30
+	// 活动
31
+	actID, _ := c.GetInt(":actid")
32
+
33
+	// 分页
34
+	page, _ := c.GetInt("page")
35
+	if page < 1 {
36
+		page = 1
37
+	}
38
+	page -= 1
39
+
40
+	// tab 类型
41
+	tab, _ := c.GetInt("tab")
42
+
43
+	// 搜索条件
44
+	search := c.GetString("q")
45
+	name := search
46
+	memID, err := strconv.Atoi(search)
47
+	if err != nil {
48
+		memID = 0
49
+	} else {
50
+		name = ""
51
+	}
52
+
53
+	list, err := c.db.List(actID, name, memID, tab, page)
54
+	if err != nil {
55
+		c.ResponseError(errors.New("查询列表失败, 请重试"))
56
+	}
57
+
58
+	c.ResponseJSON(list)
59
+}

+ 15
- 0
main.go 查看文件

@@ -0,0 +1,15 @@
1
+package main
2
+
3
+import (
4
+	_ "voting/routers"
5
+
6
+	"github.com/astaxie/beego"
7
+)
8
+
9
+func main() {
10
+	if beego.BConfig.RunMode == "dev" {
11
+		beego.BConfig.WebConfig.DirectoryIndex = true
12
+		beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
13
+	}
14
+	beego.Run()
15
+}

+ 20
- 0
models/common.go 查看文件

@@ -0,0 +1,20 @@
1
+package models
2
+
3
+const (
4
+	StatusReady = iota
5
+	StatusNormal
6
+)
7
+
8
+const (
9
+	PageNum = 10
10
+)
11
+
12
+// Insert datas
13
+func Insert(t ...interface{}) (int64, error) {
14
+	return DBEngine.Insert(t...)
15
+}
16
+
17
+// Update datas
18
+func Update(t interface{}, cols []string, query string, args ...interface{}) (int64, error) {
19
+	return DBEngine.Where(query, args...).Cols(cols...).Update(t)
20
+}

+ 27
- 0
models/model/ta_voting_activities.go 查看文件

@@ -0,0 +1,27 @@
1
+package model
2
+
3
+import (
4
+	"time"
5
+)
6
+
7
+type TaVotingActivities struct {
8
+	ActivityId int       `xorm:"not null pk autoincr SMALLINT(6)"`
9
+	RuleId     int       `xorm:"SMALLINT(6)"`
10
+	Music      string    `xorm:"TEXT"`
11
+	Name       string    `xorm:"VARCHAR(300)"`
12
+	Thumb      string    `xorm:"TEXT"`
13
+	Banner     string    `xorm:"TEXT"`
14
+	Desc       string    `xorm:"TEXT"`
15
+	Rules      string    `xorm:"TEXT"`
16
+	StartTime  time.Time `xorm:"DATETIME"`
17
+	EndTime    time.Time `xorm:"DATETIME"`
18
+	Remark     string    `xorm:"VARCHAR(500)"`
19
+	CreateDate time.Time `xorm:"DATETIME"`
20
+	Status     int       `xorm:"SMALLINT(6)"`
21
+	Members    int       `xorm:"INT(11)"`
22
+	StartId    int       `xorm:"INT(11)"`
23
+	AfterVote  string    `xorm:"TEXT"`
24
+	ShareTitle string    `xorm:"VARCHAR(100)"`
25
+	ShareIcon  string    `xorm:"TEXT"`
26
+	ShareDesc  string    `xorm:"VARCHAR(200)"`
27
+}

+ 19
- 0
models/model/ta_voting_member.go 查看文件

@@ -0,0 +1,19 @@
1
+package model
2
+
3
+import (
4
+	"time"
5
+)
6
+
7
+type TaVotingMember struct {
8
+	MemberId   int       `xorm:"not null pk INT(11)"`
9
+	ActivityId int       `xorm:"not null pk SMALLINT(6)"`
10
+	Name       string    `xorm:"VARCHAR(200)"`
11
+	Phone      string    `xorm:"VARCHAR(20)"`
12
+	Addr       string    `xorm:"TEXT"`
13
+	Message    string    `xorm:"TEXT"`
14
+	Remark     string    `xorm:"VARCHAR(500)"`
15
+	CreateDate time.Time `xorm:"DATETIME"`
16
+	Status     int       `xorm:"SMALLINT(6)"`
17
+	Vote       int       `xorm:"INT(11)"`
18
+	Openid     string    `xorm:"VARCHAR(64)"`
19
+}

+ 16
- 0
models/model/ta_voting_member_photos.go 查看文件

@@ -0,0 +1,16 @@
1
+package model
2
+
3
+import (
4
+	"time"
5
+)
6
+
7
+type TaVotingMemberPhotos struct {
8
+	PhotoId    int       `xorm:"not null pk autoincr INT(11)"`
9
+	MemberId   int       `xorm:"INT(11)"`
10
+	ActivityId int       `xorm:"SMALLINT(6)"`
11
+	Photo      string    `xorm:"TEXT"`
12
+	Type       string    `xorm:"VARCHAR(20)"`
13
+	Desc       string    `xorm:"VARCHAR(300)"`
14
+	CreateDate time.Time `xorm:"DATETIME"`
15
+	Status     int       `xorm:"SMALLINT(6)"`
16
+}

+ 15
- 0
models/model/ta_voting_records.go 查看文件

@@ -0,0 +1,15 @@
1
+package model
2
+
3
+import (
4
+	"time"
5
+)
6
+
7
+type TaVotingRecords struct {
8
+	VoteId     int       `xorm:"not null pk autoincr INT(11)"`
9
+	MemberId   int       `xorm:"INT(11)"`
10
+	ActivityId int       `xorm:"SMALLINT(6)"`
11
+	VoteDate   time.Time `xorm:"DATETIME"`
12
+	NickName   string    `xorm:"VARCHAR(200)"`
13
+	Avatar     string    `xorm:"TEXT"`
14
+	Openid     string    `xorm:"VARCHAR(64)"`
15
+}

+ 8
- 0
models/model/ta_voting_rules.go 查看文件

@@ -0,0 +1,8 @@
1
+package model
2
+
3
+type TaVotingRules struct {
4
+	RuleId    int    `xorm:"not null pk autoincr SMALLINT(6)"`
5
+	RuleValue string `xorm:"TEXT"`
6
+	Desc      string `xorm:"TEXT"`
7
+	Status    int    `xorm:"SMALLINT(6)"`
8
+}

+ 54
- 0
models/models.go 查看文件

@@ -0,0 +1,54 @@
1
+package models
2
+
3
+import (
4
+	"voting/utils"
5
+
6
+	"github.com/astaxie/beego"
7
+	_ "github.com/go-sql-driver/mysql"
8
+	"github.com/go-xorm/xorm"
9
+)
10
+
11
+var (
12
+	DBEngine *xorm.Engine
13
+	logger   *utils.Logger
14
+)
15
+
16
+func InitDB() {
17
+	DBEngine = NewDBEngine()
18
+	logger = utils.GetLogger()
19
+}
20
+
21
+// NewDBEngine 初始化数据库连接
22
+func NewDBEngine() *xorm.Engine {
23
+	dbType := "mysql"
24
+	dns := getMySQLDNS()
25
+
26
+	engine, err := xorm.NewEngine(dbType, dns)
27
+	// engine.ShowSQL()
28
+
29
+	if err != nil {
30
+		panic(err)
31
+	}
32
+
33
+	return engine
34
+}
35
+
36
+func getMySQLDNS() (dns string) {
37
+	conProt := beego.AppConfig.DefaultString("db::con_protocol", "tcp")
38
+	dbAddr := beego.AppConfig.DefaultString("db::db_addr", "localhost")
39
+	dbPort := beego.AppConfig.DefaultString("db::db_port", "3306")
40
+	userName := beego.AppConfig.DefaultString("db::username", "root")
41
+	password := beego.AppConfig.String("db::password")
42
+	database := beego.AppConfig.String("db::database")
43
+	charSet := beego.AppConfig.DefaultString("db::char_set", "utf8")
44
+
45
+	dns = userName
46
+
47
+	if len(password) > 0 {
48
+		dns += ":" + password
49
+	}
50
+
51
+	dns += "@" + conProt + "(" + dbAddr + ":" + dbPort + ")" + "/" + database + "?charset=" + charSet
52
+
53
+	return
54
+}

+ 330
- 0
models/voting.go 查看文件

@@ -0,0 +1,330 @@
1
+package models
2
+
3
+import (
4
+	"encoding/json"
5
+	"errors"
6
+	"strconv"
7
+	"time"
8
+	"voting/models/model"
9
+)
10
+
11
+const (
12
+	// 默认图片类型
13
+	DefaultPhotoType = "main"
14
+
15
+	// 列表排序类型
16
+	OrderByDate = 1
17
+	OrderByVote = 2
18
+
19
+	// 活动限制规则
20
+	RuleOfLimitEveryday = "limiteveryday"
21
+)
22
+
23
+var (
24
+	ErrVoteLimit        = errors.New("votelimit")
25
+	ErrActivityNotStart = errors.New("Activity not start")
26
+	ErrActivityIsOver   = errors.New("Activity is over")
27
+)
28
+
29
+type VotingModel struct{}
30
+
31
+type MemInfo struct {
32
+	model.TaVotingMember `xorm:"extends"`
33
+	Photo                string `xorm:"TEXT"`
34
+	Rank                 int
35
+}
36
+
37
+// List 报名列表
38
+func (m *VotingModel) List(actID int, name string, id, orderby, page int) (list []MemInfo, err error) {
39
+	query := `
40
+		SELECT t.*, s.photo
41
+		FROM
42
+			ta_voting_member t
43
+		JOIN ta_voting_member_photos s ON t.member_id = s.member_id
44
+			AND t.activity_id = s.activity_id
45
+			AND s.type = '` + DefaultPhotoType + `'
46
+			AND s.status = ` + strconv.Itoa(StatusNormal) + `
47
+		Where t.activity_id = '` + strconv.Itoa(actID) + `' AND 
48
+	`
49
+
50
+	// 依据 ID 或者 名称查询
51
+	// 全部列表的时候, 名称为空即可
52
+	if id != 0 {
53
+		query += `t.member_id = ` + strconv.Itoa(id)
54
+	} else {
55
+		query += `t.name like "%` + name + `%"`
56
+	}
57
+	query += " and t.status = " + strconv.Itoa(StatusNormal)
58
+
59
+	// 排序
60
+	if orderby == OrderByVote {
61
+		query += " order by t.vote desc"
62
+	} else {
63
+		query += " order by t.create_date desc"
64
+	}
65
+
66
+	// 分页
67
+	offset := (page - 1) * PageNum
68
+	if offset < 0 {
69
+		offset = 0
70
+	}
71
+
72
+	query += " limit " + strconv.Itoa(PageNum) + " offset " + strconv.Itoa(offset)
73
+
74
+	if err = DBEngine.SQL(query).Find(&list); err != nil {
75
+		logger.Error("查询活动列表失败: ", err)
76
+		return
77
+	}
78
+
79
+	return
80
+}
81
+
82
+// GetActivityInfo 查询活动
83
+func (m *VotingModel) GetActivityInfo(actID int) (act *model.TaVotingActivities, err error) {
84
+	_, err = DBEngine.Where("t.activity_id=?", actID).Get(act)
85
+	if err != nil {
86
+		logger.Error("查询活动失败: ", err)
87
+		return
88
+	}
89
+
90
+	return
91
+}
92
+
93
+// GetMemInfo 获取报名人员信息
94
+func (m *VotingModel) GetMemInfo(actID, memID int) (mem *MemInfo, err error) {
95
+	query := `
96
+		SELECT t.*, s.photo
97
+		FROM
98
+			ta_voting_member t
99
+		JOIN ta_voting_member_photos s ON t.member_id = s.member_id
100
+			AND t.activity_id = s.activity_id
101
+			AND s.type = '` + DefaultPhotoType + `'
102
+			AND s.status = ` + strconv.Itoa(StatusNormal) + `
103
+		Where t.activity_id = ? AND t.member_id = ?
104
+	`
105
+
106
+	if _, err = DBEngine.SQL(query, actID, memID).Get(mem); err != nil {
107
+		logger.Error("获取报名人员失败: ", err)
108
+		return
109
+	}
110
+
111
+	// 获取排名
112
+	cnt, e := DBEngine.Where("activity_id=?", actID).
113
+		And("status=?", StatusNormal).
114
+		And("vote>?", mem.Vote).
115
+		Count()
116
+	if e != nil {
117
+		logger.Error("获取报名人员排名失败:", err)
118
+
119
+		// -1 代表发生错误
120
+		mem.Rank = -1
121
+		return
122
+	}
123
+
124
+	mem.Rank = int(cnt)
125
+	return
126
+}
127
+
128
+// Registe 报名保存
129
+func (m *VotingModel) Registe(memInfo *MemInfo) error {
130
+	if memInfo.ActivityId == 0 {
131
+		return errors.New("活动报名失败, 没有指定活动内容")
132
+	}
133
+
134
+	now := time.Now().Local()
135
+
136
+	mem := memInfo.TaVotingMember
137
+	photo := model.TaVotingMemberPhotos{
138
+		ActivityId: mem.ActivityId,
139
+		Photo:      memInfo.Photo,
140
+		Type:       DefaultPhotoType,
141
+		CreateDate: now,
142
+		Status:     StatusNormal,
143
+	}
144
+
145
+	// 检查已有最大值
146
+	maxID, err := m.getMaxMemID(mem.ActivityId)
147
+	if err != nil {
148
+		return err
149
+	}
150
+
151
+	mem.MemberId = int(maxID + 1)
152
+	photo.MemberId = mem.MemberId
153
+	memInfo.MemberId = mem.MemberId
154
+
155
+	if _, err := Insert(&photo); err != nil {
156
+		logger.Error("保存活动报名图片失败: ", err)
157
+		return err
158
+	}
159
+
160
+	if _, err := Insert(&mem); err != nil {
161
+		logger.Error("保存活动报名失败: ", err)
162
+		return err
163
+	}
164
+
165
+	// 反更新报名人数
166
+	// TODO
167
+
168
+	return nil
169
+}
170
+
171
+func (m *VotingModel) getMaxMemID(actID int) (maxID int64, err error) {
172
+	query := `
173
+		SELECT
174
+			max(start_id) as max_id
175
+		FROM
176
+			(
177
+				SELECT
178
+					max(t.member_id) AS start_id
179
+				FROM
180
+					ta_voting_member t
181
+				UNION
182
+					SELECT
183
+						s.start_id
184
+					FROM
185
+						ta_voting_activities s
186
+					WHERE
187
+						s.activity_id = ?
188
+			) a
189
+	`
190
+
191
+	if _, err = DBEngine.SQL(query, actID).Get(&maxID); err != nil {
192
+		logger.Error("查询活动报名最大ID失败: ", err)
193
+		return
194
+	}
195
+
196
+	return
197
+}
198
+
199
+// Vote 投票
200
+func (m *VotingModel) Vote(rec *model.TaVotingRecords) error {
201
+	// 校验活动
202
+	if err := m.checkActivity(rec.ActivityId); err != nil {
203
+		return err
204
+	}
205
+
206
+	rule, err := m.getActivityRule(rec.ActivityId)
207
+	if err != nil {
208
+		return err
209
+	}
210
+
211
+	var ruleParam map[string]interface{}
212
+	if err := json.Unmarshal([]byte(rule.RuleValue), &ruleParam); err != nil {
213
+		logger.Error("转换活动规则类型失败: ", err)
214
+		return err
215
+	}
216
+
217
+	// 如果有每天最大投票限制
218
+	if v, ok := ruleParam[RuleOfLimitEveryday]; ok {
219
+		limitEveryday := int64(v.(float64))
220
+
221
+		voteNum, err := m.getVoteNumOfMember(rec)
222
+		if err != nil {
223
+			return err
224
+		}
225
+
226
+		if voteNum >= limitEveryday {
227
+			return ErrVoteLimit
228
+		}
229
+	}
230
+
231
+	// 每天投票
232
+	rec.VoteDate = time.Now().Local()
233
+	if _, err := Insert(rec); err != nil {
234
+		logger.Error("活动投票失败: ", err)
235
+		return err
236
+	}
237
+
238
+	// 反更新次数
239
+	if err := m.updateVoteOfMember(rec.ActivityId, rec.MemberId); err != nil {
240
+		return err
241
+	}
242
+
243
+	return nil
244
+}
245
+
246
+// 获取投票规则
247
+func (m *VotingModel) getActivityRule(actID int) (rule *model.TaVotingRules, err error) {
248
+	query := `
249
+		SELECT
250
+			t.*
251
+		FROM
252
+			ta_voting_rules t
253
+		JOIN ta_voting_activities s ON t.rule_id = s.rule_id
254
+		AND s.activity_id = ?
255
+	`
256
+
257
+	if _, err = DBEngine.SQL(query, actID).Get(rule); err != nil {
258
+		logger.Error("查询活动规则失败: ", err)
259
+		return
260
+	}
261
+
262
+	if rule.RuleValue == "" {
263
+		rule.RuleValue = "{}"
264
+	}
265
+
266
+	return
267
+}
268
+
269
+// 获取当前人员投票数
270
+func (m *VotingModel) getVoteNumOfMember(rec *model.TaVotingRecords) (num int64, err error) {
271
+	query := `
272
+		SELECT
273
+			*
274
+		FROM
275
+			ta_voting_records t
276
+		WHERE
277
+			t.activity_id = ?
278
+		AND t.member_id = ?
279
+		AND t.openid = ?
280
+		AND date_format(t.vote_date, "%Y-%m-%d") = date_format(now(), "%Y-%m-%d")
281
+	`
282
+
283
+	num, err = DBEngine.SQL(query, rec.ActivityId, rec.MemberId, rec.Openid).Count()
284
+	if err != nil {
285
+		logger.Error("查询人员投票次数失败: ", err)
286
+		return
287
+	}
288
+
289
+	return
290
+}
291
+
292
+// 反更新投票次数
293
+func (m *VotingModel) updateVoteOfMember(actID, memID int) error {
294
+	query := `
295
+	UPDATE ta_voting_member t
296
+		SET t.vote = t.vote + 1
297
+		WHERE
298
+			t.activity_id = ?
299
+		AND t.member_id = ?
300
+	`
301
+
302
+	if _, err := DBEngine.Exec(query, actID, memID); err != nil {
303
+		logger.Error("更新投票次数失败: ", err)
304
+		return err
305
+	}
306
+
307
+	return nil
308
+}
309
+
310
+// 校验活动
311
+// 只校验活动时间
312
+func (m *VotingModel) checkActivity(actID int) error {
313
+	act := model.TaVotingActivities{}
314
+
315
+	if _, err := DBEngine.Where("activity_id=?", actID).Get(&act); err != nil {
316
+		logger.Error("校验活动失败: ", err)
317
+		return err
318
+	}
319
+
320
+	now := time.Now().Local()
321
+	if act.StartTime.After(now) {
322
+		return ErrActivityNotStart
323
+	}
324
+
325
+	if act.EndTime.Before(now) {
326
+		return ErrActivityIsOver
327
+	}
328
+
329
+	return nil
330
+}

+ 25
- 0
routers/router.go 查看文件

@@ -0,0 +1,25 @@
1
+// @APIVersion 1.0.0
2
+// @Title beego Test API
3
+// @Description beego has a very cool tools to autogenerate documents for your API
4
+// @Contact astaxie@gmail.com
5
+// @TermsOfServiceUrl http://beego.me/
6
+// @License Apache 2.0
7
+// @LicenseUrl http://www.apache.org/licenses/LICENSE-2.0.html
8
+package routers
9
+
10
+import (
11
+	"voting/controllers"
12
+
13
+	"github.com/astaxie/beego"
14
+)
15
+
16
+func init() {
17
+	ns := beego.NewNamespace("/api",
18
+		beego.NSNamespace("/file",
19
+			beego.NSInclude(
20
+				&controllers.ObjectController{},
21
+			),
22
+		),
23
+	)
24
+	beego.AddNamespace(ns)
25
+}

+ 38
- 0
tests/default_test.go 查看文件

@@ -0,0 +1,38 @@
1
+package test
2
+
3
+import (
4
+	"net/http"
5
+	"net/http/httptest"
6
+	"testing"
7
+	"runtime"
8
+	"path/filepath"
9
+	_ "voting/routers"
10
+
11
+	"github.com/astaxie/beego"
12
+	. "github.com/smartystreets/goconvey/convey"
13
+)
14
+
15
+func init() {
16
+	_, file, _, _ := runtime.Caller(1)
17
+	apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator))))
18
+	beego.TestBeegoInit(apppath)
19
+}
20
+
21
+// TestGet is a sample to run an endpoint test
22
+func TestGet(t *testing.T) {
23
+	r, _ := http.NewRequest("GET", "/v1/object", nil)
24
+	w := httptest.NewRecorder()
25
+	beego.BeeApp.Handlers.ServeHTTP(w, r)
26
+
27
+	beego.Trace("testing", "TestGet", "Code[%d]\n%s", w.Code, w.Body.String())
28
+
29
+	Convey("Subject: Test Station Endpoint\n", t, func() {
30
+	        Convey("Status Code Should Be 200", func() {
31
+	                So(w.Code, ShouldEqual, 200)
32
+	        })
33
+	        Convey("The Result Should Not Be Empty", func() {
34
+	                So(w.Body.Len(), ShouldBeGreaterThan, 0)
35
+	        })
36
+	})
37
+}
38
+

+ 85
- 0
utils/aliyun.go 查看文件

@@ -0,0 +1,85 @@
1
+package utils
2
+
3
+import (
4
+	"errors"
5
+	"net/http"
6
+	"strings"
7
+
8
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
9
+	"github.com/astaxie/beego"
10
+)
11
+
12
+var ossCli *oss.Client
13
+
14
+func OssInit() {
15
+	logger := GetLogger()
16
+
17
+	cert, err := beego.AppConfig.GetSection("alioss")
18
+	if err != nil {
19
+		logger.Error("读取阿里 OSS 配置失败: ", err)
20
+		return
21
+	}
22
+
23
+	OssClientSingleton(cert["Endpoint"], cert["AccessKeyId"], cert["AccessKeySecret"])
24
+}
25
+
26
+func OssClientSingleton(endpoint, accessKeyID, accessKeySecret string) {
27
+	if ossCli == nil {
28
+		var err error
29
+		logger := GetLogger()
30
+		ossCli, err = oss.New(endpoint, accessKeyID, accessKeySecret)
31
+		if err != nil {
32
+			logger.Error("初始化阿里 OSS 失败: ", err)
33
+		}
34
+	}
35
+}
36
+
37
+// GetOssClient 获取 OSS 客户端
38
+func GetOssClient() *oss.Client {
39
+	return ossCli
40
+}
41
+
42
+// UploadFileToBucket 简单上传文件
43
+func UploadFileToBucket(req *http.Request, fromFront string, nameSuffix ...string) (string, error) {
44
+	logger := GetLogger()
45
+	bucket := beego.AppConfig.String("alioss::Bucket")
46
+	endpoint := beego.AppConfig.String("alioss::Endpoint")
47
+
48
+	if ossCli == nil {
49
+		logger.Error("未成功初始化OSS 客户端")
50
+		return "", errors.New("未成功初始化OSS 客户端")
51
+	}
52
+
53
+	bkt, err := ossCli.Bucket(bucket)
54
+	if err != nil {
55
+		logger.Error("初始化OSS Bucket 失败: ", err)
56
+		return "", errors.New("初始化OSS Bucket 失败")
57
+	}
58
+
59
+	file, fh, err := req.FormFile(fromFront)
60
+	if err != nil {
61
+		logger.Error("读取文件失败: ", err)
62
+		return "", errors.New("未读取到文件或读取失败")
63
+	}
64
+	defer file.Close()
65
+
66
+	fname := fh.Filename
67
+	if len(nameSuffix) > 0 && nameSuffix[0] != "" {
68
+		fArr := strings.Split(fname, ".")
69
+		fArr[0] = fArr[0] + nameSuffix[0]
70
+		fname = strings.Join(fArr, ".")
71
+	}
72
+
73
+	err = bkt.PutObject(fname, file)
74
+	if err != nil {
75
+		logger.Error("上传文件到 OSS 失败: ", err)
76
+		return "", errors.New("上传文件到 OSS 失败")
77
+	}
78
+
79
+	return strings.Join([]string{
80
+		"http://",
81
+		bucket + ".",
82
+		endpoint + "/",
83
+		fname,
84
+	}, ""), nil
85
+}

+ 67
- 0
utils/log.go 查看文件

@@ -0,0 +1,67 @@
1
+package utils
2
+
3
+import (
4
+	"github.com/astaxie/beego"
5
+)
6
+
7
+type Logger struct{}
8
+
9
+var logger *Logger
10
+
11
+func GetLogger() *Logger {
12
+	if logger == nil {
13
+		logger = new(Logger)
14
+	}
15
+
16
+	return logger
17
+}
18
+
19
+func (l *Logger) Critical(v string, o ...interface{}) {
20
+	f := []interface{}{v}
21
+
22
+	if o != nil && len(o) > 0 {
23
+		f = append(f, o...)
24
+	}
25
+
26
+	beego.Critical(f)
27
+}
28
+
29
+func (l *Logger) Error(v string, o ...interface{}) {
30
+	f := []interface{}{v}
31
+
32
+	if o != nil && len(o) > 0 {
33
+		f = append(f, o...)
34
+	}
35
+
36
+	beego.Error(f)
37
+}
38
+
39
+func (l *Logger) Warning(v string, o ...interface{}) {
40
+	f := []interface{}{v}
41
+
42
+	if o != nil && len(o) > 0 {
43
+		f = append(f, o...)
44
+	}
45
+
46
+	beego.Warning(f)
47
+}
48
+
49
+func (l *Logger) Info(v string, o ...interface{}) {
50
+	f := []interface{}{v}
51
+
52
+	if o != nil && len(o) > 0 {
53
+		f = append(f, o...)
54
+	}
55
+
56
+	beego.Info(f)
57
+}
58
+
59
+func (l *Logger) Debug(v string, o ...interface{}) {
60
+	f := []interface{}{v}
61
+
62
+	if o != nil && len(o) > 0 {
63
+		f = append(f, o...)
64
+	}
65
+
66
+	beego.Debug(f)
67
+}

+ 100
- 0
utils/wechat.go 查看文件

@@ -0,0 +1,100 @@
1
+package utils
2
+
3
+import (
4
+	"github.com/astaxie/beego"
5
+	"github.com/zjxpcyc/wechat/wx"
6
+)
7
+
8
+var wxClients *wx.Client
9
+
10
+type WechatUser struct {
11
+	OpenID     string  `json:"openid"`
12
+	NickName   string  `json:"nickname"`
13
+	Sex        float64 `json:"sex"`
14
+	Province   string  `json:"province"`
15
+	City       string  `json:"city"`
16
+	Country    string  `json:"country"`
17
+	HeadImgURL string  `json:"headimgurl"`
18
+	UnionID    string  `json:"unionid"`
19
+}
20
+
21
+// WxClientSingleton 初始化
22
+func WxClientSingleton(cert map[string]string) {
23
+	if wxClients == nil {
24
+		wxClients = wx.NewClient(cert)
25
+	}
26
+}
27
+
28
+func GetWxClient() *wx.Client {
29
+	return wxClients
30
+}
31
+
32
+// GetWxAppID 获取微信ID
33
+func GetWxAppID() string {
34
+	if wxClients != nil {
35
+		return wxClients.GetAppID()
36
+	} else {
37
+		return ""
38
+	}
39
+}
40
+
41
+func WechatInit() {
42
+	logger := GetLogger()
43
+	wx.SetLogInst(logger)
44
+
45
+	cert, err := beego.AppConfig.GetSection("wechat")
46
+	if err != nil {
47
+		logger.Error("初始化微信失败: ", err)
48
+	}
49
+
50
+	WxClientSingleton(cert)
51
+}
52
+
53
+// MapToWechatUser 映射微信人员
54
+func MapToWechatUser(data map[string]interface{}) *WechatUser {
55
+	subscribe, has := data["subscribe"]
56
+	if has {
57
+		if subscribe == nil {
58
+			return nil
59
+		}
60
+
61
+		sub := subscribe.(float64)
62
+		if sub == 0 {
63
+			return nil
64
+		}
65
+	}
66
+
67
+	user := WechatUser{
68
+		OpenID: data["openid"].(string),
69
+	}
70
+
71
+	if data["sex"] != nil {
72
+		user.Sex = data["sex"].(float64)
73
+	}
74
+
75
+	if data["nickname"] != nil {
76
+		user.NickName = data["nickname"].(string)
77
+	}
78
+
79
+	if data["province"] != nil {
80
+		user.Province = data["province"].(string)
81
+	}
82
+
83
+	if data["city"] != nil {
84
+		user.City = data["city"].(string)
85
+	}
86
+
87
+	if data["country"] != nil {
88
+		user.Country = data["country"].(string)
89
+	}
90
+
91
+	if data["headimgurl"] != nil {
92
+		user.HeadImgURL = data["headimgurl"].(string)
93
+	}
94
+
95
+	if data["unionid"] != nil {
96
+		user.UnionID = data["unionid"].(string)
97
+	}
98
+
99
+	return &user
100
+}