Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@ -0,0 +1,61 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"img_test.go",
"question_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/answer/conf:go_default_library",
"//app/admin/main/answer/model:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"img.go",
"question.go",
"service.go",
],
importpath = "go-common/app/admin/main/answer/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/answer/conf:go_default_library",
"//app/admin/main/answer/dao:go_default_library",
"//app/admin/main/answer/model:go_default_library",
"//library/cache:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/text/translate/chinese:go_default_library",
"//vendor/github.com/golang/freetype:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,124 @@
package service
import (
"bufio"
"bytes"
"context"
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"fmt"
"image/jpeg"
"net/http"
"strconv"
"time"
"go-common/library/log"
"go-common/library/text/translate/chinese"
"github.com/golang/freetype"
)
var (
allOrders [][]int
x = []int{0, 1, 2, 3}
bname = "member"
accessKey = "3d34b1ea1dbbb0ca"
secretKey = "d4caa344f3b115e302033b05dd0aa4"
_template = "%s\n%s\n%s\n%d\n"
fileFormat = "v3_%s_A-%s_B-%s_C-%s_D-%s_%s"
bfsURL = "http://bfs.bilibili.co/bfs/%s/%s"
platform = map[string]bool{"H5": true, "PC": false}
language = []string{"zh-CN", "zh-TW"}
)
// generate 全排列组合算法 4*3*2*1 种情况
func (s *Service) generate(c context.Context, a []int, begin int, end int) {
if begin == end {
allOrders = append(allOrders, []int{a[0], a[1], a[2], a[3]})
return
}
for i := begin; i <= end; i++ {
a[begin], a[i] = a[i], a[begin]
s.generate(c, a, begin+1, end)
a[begin], a[i] = a[i], a[begin]
}
return
}
// GenerateImage .
func (s *Service) GenerateImage(c context.Context) {
// 把有效id放redis
ids, _ := s.dao.IDsByState(c)
s.CreateBFSImg(c, ids)
}
// CreateBFSImg .
func (s *Service) CreateBFSImg(c context.Context, ids []int64) {
for _, qid := range ids {
log.Error("qid_%d", qid)
que, err := s.dao.QueByID(c, qid)
log.Error("que:%+v", que)
if err != nil {
log.Error("get question(%d), err(%v)", qid, err)
}
for _, langv := range language {
if langv == "zh-TW" {
que.Question = chinese.Convert(c, que.Question)
que.Ans[0] = chinese.Convert(c, que.Ans[0])
que.Ans[1] = chinese.Convert(c, que.Ans[1])
que.Ans[2] = chinese.Convert(c, que.Ans[2])
que.Ans[3] = chinese.Convert(c, que.Ans[3])
}
for plat, platv := range platform {
for _, order := range allOrders {
log.Error("allOrders(len:%d) %s_%s_%s_%s_%s_%s, %+v", len(allOrders), strconv.FormatInt(qid, 10), que.Ans[order[0]], que.Ans[order[1]], que.Ans[order[2]], que.Ans[order[3]], plat, que)
quec := s.dao.QueConf(platv)
imgh := s.dao.Height(quec, que.Question, len(que.Ans))
r := s.dao.Board(imgh)
imgc := s.dao.Context(r, s.c.Answer.FontFilePath)
pt := freetype.Pt(0, int(quec.Fontsize))
s.dao.DrawQue(imgc, que.Question, quec, &pt)
as := [4]string{que.Ans[order[0]], que.Ans[order[1]], que.Ans[order[2]], que.Ans[order[3]]}
s.dao.DrawAns(imgc, quec, as, &pt)
buf := new(bytes.Buffer)
jpeg.Encode(buf, r, nil)
bufReader := bufio.NewReader(buf)
ts := time.Now().Unix()
tm := time.Unix(ts, 0)
m := md5.New()
m.Write([]byte(fmt.Sprintf(fileFormat, strconv.FormatInt(qid, 10), que.Ans[order[0]], que.Ans[order[1]], que.Ans[order[2]], que.Ans[order[3]], plat)))
fname := hex.EncodeToString(m.Sum(nil)) + ".jpg"
if s.c.Answer.Debug {
fname = fmt.Sprintf("debug_%s", fname)
}
//imgName = append(imgName, fname)
client := &http.Client{}
content := fmt.Sprintf(_template, "PUT", bname, fname, ts)
mac := hmac.New(sha1.New, []byte(secretKey))
mac.Write([]byte(content))
sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
req, _ := http.NewRequest(http.MethodPut, fmt.Sprintf(bfsURL, bname, fname), bufReader)
req.Host = "bfs.bilibili.co"
req.Header.Add("Date", tm.Format("2006-01-02 03:04:05"))
req.Header.Add("Authorization", accessKey+":"+sign+":"+strconv.FormatInt(ts, 10))
req.Header.Set("Content-Type", "image/jpeg")
resp, _ := client.Do(req)
//defer resp.Body.Close()
if err != nil {
log.Error("qid %s bfs upload failed error(%s)", strconv.FormatInt(qid, 10), err)
//return
}
log.Info("upload img(%d), %s, %+v", qid, fname, resp)
time.Sleep(time.Millisecond * 50)
}
}
}
}
}

View File

@ -0,0 +1,31 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/answer/model"
"go-common/library/xstr"
. "github.com/smartystreets/goconvey/convey"
)
func TestServiceImg(t *testing.T) {
Convey("TestServiceImg", t, func() {
ids, err := xstr.SplitInts("35")
s.CreateBFSImg(context.TODO(), ids)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestAddImg
func TestAddImg(t *testing.T) {
q := &model.QuestionDB{ID: 1, State: 1, Question: "test2333"}
Convey("TestAddImgQuestionAdd", t, func() {
err := s.QuestionAdd(context.TODO(), q)
So(err, ShouldBeNil)
})
Convey("TestAddImg", t, func() {
s.GenerateImage(context.TODO())
})
}

View File

@ -0,0 +1,282 @@
package service
import (
"context"
"encoding/csv"
"io"
"mime/multipart"
"strconv"
"strings"
"time"
"go-common/app/admin/main/answer/model"
"go-common/library/ecode"
"go-common/library/log"
)
// QuestionList .
func (s *Service) QuestionList(c context.Context, arg *model.ArgQue) (res *model.QuestionPage, err error) {
res = &model.QuestionPage{}
if res.Total, err = s.dao.QuestionCount(c, arg); err != nil {
return
}
res.Items = []*model.QuestionDB{}
if res.Total > 0 {
if res.Items, err = s.dao.QuestionList(c, arg); err != nil {
return
}
}
return
}
// UpdateStatus update question state
func (s *Service) UpdateStatus(c context.Context, qid int64, state int8, operator string) (err error) {
var (
r int64
q *model.Question
)
if q, err = s.dao.QueByID(c, qid); err != nil {
log.Error("dao QueByID(%d) error(%v)", qid, err)
return
}
if q == nil || q.State == state {
return
}
if r, err = s.dao.UpdateStatus(c, state, qid, operator); err != nil || r != 1 {
return
}
return
}
// BatchUpdateState bacth update question state.
func (s *Service) BatchUpdateState(c context.Context, qids []int64, state int8, operator string) (err error) {
for _, id := range qids {
s.UpdateStatus(c, id, state, operator)
}
return
}
// Types question type
func (s *Service) Types(c context.Context) (res []*model.TypeInfo, err error) {
return s.dao.Types(c)
}
// ReadCsv read csv file
func (s *Service) ReadCsv(f multipart.File, h *multipart.FileHeader) (rs [][]string, err error) {
r := csv.NewReader(f)
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Error("upload question ReadCsv error(%v)", err)
break
}
if len(record) == model.ArgsCount {
rs = append(rs, record)
}
}
return
}
// UploadQsts upload questions
func (s *Service) UploadQsts(c context.Context, f multipart.File, h *multipart.FileHeader, operator string) (msg string, err error) {
defer f.Close()
if h != nil && !strings.HasSuffix(h.Filename, ".csv") {
msg = "not csv file."
return
}
sz, ok := f.(model.Sizer)
if !ok {
msg = "get file size faild."
return
}
size := sz.Size()
if size > model.FileMaxSize {
msg = "file size more than 2M."
return
}
rs, err := s.ReadCsv(f, h)
log.Info("file %s, len(%d)", h.Filename, len(rs))
if len(rs) == 0 || len(rs) > model.MaxCount {
msg = "file size count is 0 or more than " + strconv.FormatInt(model.MaxCount, 10)
return
}
for _, r := range rs {
typeID, err := strconv.ParseInt(r[0], 10, 8)
if err != nil {
log.Error("strconv.ParseInt(%+v) err(%v)", r[0], err)
}
if err == nil {
q := &model.QuestionDB{
TypeID: int8(typeID),
Question: r[1],
Ans1: r[2],
Ans2: r[3],
Ans3: r[4],
Ans4: r[5],
Operator: operator,
}
if err = s.QuestionAdd(c, q); err != nil {
log.Error("s.QuestionAdd(%+v) error(%v)", q, err)
}
}
}
return
}
// QuestionAdd add register question
func (s *Service) QuestionAdd(c context.Context, q *model.QuestionDB) (err error) {
if len(q.Question) < model.MinQuestion || len(q.Question) > model.MaxQuestion {
err = ecode.QuestionStrNotAllow
return
}
if len(q.Ans1) < model.MinAns || len(q.Ans1) > model.MaxAns ||
len(q.Ans2) < model.MinAns || len(q.Ans2) > model.MaxAns ||
len(q.Ans3) < model.MinAns || len(q.Ans3) > model.MaxAns ||
len(q.Ans4) < model.MinAns || len(q.Ans4) > model.MaxAns {
err = ecode.QuestionAnsNotAllow
return
}
if q.Tips != "" && (len(q.Tips) < model.MinTips || len(q.Tips) > model.MaxTips) {
err = ecode.QuestionTipsNotAllow
return
}
if q.TypeID <= 0 {
err = ecode.QuestionTypeNotAllow
return
}
// only sourport text question
q.MediaType = model.TextMediaType
q.State = model.PassCheck
q.Ctime = time.Now()
if _, err = s.dao.QuestionAdd(c, q); err != nil {
return
}
qid := q.ID
s.eventChan.Save(func() {
s.CreateBFSImg(context.Background(), []int64{qid})
})
return
}
func (s *Service) loadtypes(c context.Context) (t map[int64]*model.TypeInfo, err error) {
var tys []*model.TypeInfo
tys, err = s.dao.Types(c)
if err != nil {
log.Error("s.dao.Types error(%v)", err)
return
}
t = make(map[int64]*model.TypeInfo)
for _, v := range tys {
if v.Parentid == 0 && t[v.ID] == nil {
t[v.ID] = &model.TypeInfo{ID: v.ID, Name: v.Name, Subs: []*model.SubType{}}
} else if t[v.Parentid] != nil {
t[v.Parentid].Subs = append(t[v.Parentid].Subs, &model.SubType{ID: v.ID, Name: v.Name, LabelName: v.LabelName})
}
}
return
}
// QuestionEdit .
func (s *Service) QuestionEdit(c context.Context, arg *model.QuestionDB) (aff int64, err error) {
if aff, err = s.dao.QuestionEdit(c, arg); err != nil {
return
}
s.eventChan.Save(func() {
s.CreateBFSImg(context.Background(), []int64{arg.ID})
})
return
}
// LoadTypes .
func (s *Service) LoadTypes(c context.Context) (err error) {
var allType = []*model.TypeInfo{
{ID: 1, Parentid: 0, Name: "游戏"},
{ID: 2, Parentid: 0, Name: "影视"},
{ID: 3, Parentid: 0, Name: "科技"},
{ID: 4, Parentid: 0, Name: "动画"},
{ID: 5, Parentid: 0, Name: "艺术"},
{ID: 6, Parentid: 0, Name: "流行前线"},
{ID: 7, Parentid: 0, Name: "鬼畜"},
{ID: 8, Parentid: 1, Name: "动作射击", LabelName: "游戏"},
{ID: 9, Parentid: 1, Name: "冒险格斗", LabelName: "游戏"},
{ID: 12, Parentid: 1, Name: "策略模拟 ", LabelName: "游戏"},
{ID: 13, Parentid: 1, Name: "角色扮演 ", LabelName: "游戏"},
{ID: 14, Parentid: 1, Name: "音乐体育 ", LabelName: "游戏"},
{ID: 15, Parentid: 2, Name: "纪录片 ", LabelName: "影视"},
{ID: 16, Parentid: 2, Name: "电影 ", LabelName: "影视"},
{ID: 17, Parentid: 2, Name: "电视剧 ", LabelName: "影视"},
{ID: 18, Parentid: 3, Name: "军事 ", LabelName: "科技"},
{ID: 19, Parentid: 3, Name: "地理 ", LabelName: "科技"},
{ID: 20, Parentid: 3, Name: "历史 ", LabelName: "科技"},
{ID: 21, Parentid: 3, Name: "文学 ", LabelName: "科技"},
{ID: 22, Parentid: 3, Name: "数学 ", LabelName: "科技"},
{ID: 23, Parentid: 3, Name: "物理 ", LabelName: "科技"},
{ID: 24, Parentid: 3, Name: "化学 ", LabelName: "科技"},
{ID: 25, Parentid: 3, Name: "生物 ", LabelName: "科技"},
{ID: 26, Parentid: 3, Name: "数码科技 ", LabelName: "科技"},
{ID: 27, Parentid: 4, Name: "动画声优 ", LabelName: "动画"},
{ID: 28, Parentid: 4, Name: "动漫内容 ", LabelName: "动画"},
{ID: 29, Parentid: 5, Name: "ACG音乐 ", LabelName: "艺术"},
{ID: 30, Parentid: 5, Name: "三次元音乐 ", LabelName: "艺术"},
{ID: 31, Parentid: 5, Name: "绘画 ", LabelName: "艺术"},
{ID: 32, Parentid: 6, Name: "娱乐 ", LabelName: "流行前线"},
{ID: 33, Parentid: 6, Name: "时尚 ", LabelName: "流行前线"},
{ID: 34, Parentid: 6, Name: "运动 ", LabelName: "流行前线"},
{ID: 35, Parentid: 7, Name: "鬼畜 ", LabelName: "鬼畜"},
{ID: 36, Parentid: 0, Name: "基础题", LabelName: "基础题"},
}
for _, v := range allType {
if _, err := s.dao.TypeSave(context.Background(), v); err != nil {
log.Error("s.dao.TypeSave(%+v) err(%v)", v, err)
}
}
return
}
// LoadImg .
func (s *Service) LoadImg(c context.Context) (err error) {
qss, err := s.dao.AllQS(c)
if err != nil {
log.Error("s.dao.AllQS() err(%v)", err)
}
for _, qs := range qss {
lastID := qs.ID
if err = s.eventChan.Save(func() {
s.CreateBFSImg(context.Background(), []int64{lastID})
}); err != nil {
log.Error("s.CreateBFSImg(%d) err(%v)", lastID, err)
}
}
return
}
// QueHistory .
func (s *Service) QueHistory(c context.Context, arg *model.ArgHistory) (res *model.HistoryPage, err error) {
res = &model.HistoryPage{}
if res.Total, err = s.dao.HistoryCount(c, arg); err != nil {
return
}
res.Items = []*model.AnswerHistoryDB{}
if res.Total > 0 {
if res.Items, err = s.dao.QueHistory(c, arg); err != nil {
return
}
}
return
}
// History .
func (s *Service) History(c context.Context, arg *model.ArgHistory) (res *model.HistoryPage, err error) {
if arg.Pn <= 0 || arg.Ps <= 0 {
arg.Pn, arg.Ps = 1, 1000
}
res = &model.HistoryPage{}
if res.Items, err = s.dao.HistoryES(c, arg); err != nil {
return
}
res.Total = int64(len(res.Items))
return
}

View File

@ -0,0 +1,51 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/answer/model"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceBatchUpdateState(t *testing.T) {
convey.Convey("BatchUpdateState", t, func() {
err := s.BatchUpdateState(context.TODO(), []int64{1, 2, 3}, 0, "")
convey.So(err, convey.ShouldBeNil)
})
}
func TestServiceTypes(t *testing.T) {
convey.Convey("Types", t, func() {
res, err := s.Types(context.TODO())
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceQuestionAdd(t *testing.T) {
q := &model.QuestionDB{ID: 1, Question: "test2333"}
convey.Convey("QuestionAdd", t, func() {
err := s.QuestionAdd(context.TODO(), q)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("UpdateStatus", t, func() {
err := s.UpdateStatus(context.TODO(), 1, 0, "")
convey.So(err, convey.ShouldBeNil)
})
arg := &model.ArgQue{State: 1}
convey.Convey("QuestionList", t, func() {
res, err := s.QuestionList(context.TODO(), arg)
convey.So(err, convey.ShouldBeNil)
convey.So(res, convey.ShouldNotBeNil)
})
}
func TestServiceloadtypes(t *testing.T) {
convey.Convey("loadtypes", t, func() {
t, err := s.loadtypes(context.TODO())
convey.So(err, convey.ShouldBeNil)
convey.So(t, convey.ShouldNotBeNil)
})
}

View File

@ -0,0 +1,32 @@
package service
import (
"context"
"go-common/app/admin/main/answer/conf"
"go-common/app/admin/main/answer/dao"
"go-common/library/cache"
)
// Service struct of service.
type Service struct {
c *conf.Config
dao *dao.Dao
eventChan *cache.Cache
}
// New create service instance and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
eventChan: cache.New(1, 10240),
}
s.generate(context.Background(), x, 0, len(x)-1)
return
}
// Close dao.
func (s *Service) Close() {
s.dao.Close()
}

View File

@ -0,0 +1,44 @@
package service
import (
"flag"
"go-common/app/admin/main/answer/conf"
"os"
"testing"
"github.com/smartystreets/goconvey/convey"
)
var (
s *Service
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account-law.answer-admin")
flag.Set("conf_appid", "main.account-law.answer-admin")
flag.Set("conf_token", "bec0ecd7a2799a424602f9a0daea070d")
flag.Set("tree_id", "4752")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_env", "10")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../cmd/answer-admin-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
s = New(conf.Conf)
os.Exit(m.Run())
}
func TestServiceClose(t *testing.T) {
convey.Convey("Close", t, func() {
s.Close()
})
}