Initial commit

This commit is contained in:
Donny
2019-04-22 20:46:32 +08:00
commit 49ab8aadd1
25441 changed files with 4055000 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/main/coupon/cmd:all-srcs",
"//app/admin/main/coupon/conf:all-srcs",
"//app/admin/main/coupon/dao:all-srcs",
"//app/admin/main/coupon/http:all-srcs",
"//app/admin/main/coupon/model:all-srcs",
"//app/admin/main/coupon/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,33 @@
# v1.5.1
1. 获取批次信息 读db master
# v1.5.0
1. 代金券【最优选择+兑换功能】
# v1.4.0
1. 新增大会员代金券限制商品
# v1.3.2
1. fix 代金券消息
# v1.3.1
1. 消息开关
# v1.3.0
1. 萌节活动
# v1.2.1
1. add expire day
# v1.2.0
1. 观影券后台管理
# v1.1.0
1.add business receive allowance
# v1.0.2
1. change coupon pc name
# v1.0.1
1. allowance admin
# v1.0.0
1. 劵管理后台

View File

@@ -0,0 +1,11 @@
# Owner
yubaihai
zhaogangtao
# Author
yubaihai
zhaogangtao
wangyuzhe
# Reviewer
zhaogangtao

View File

@@ -0,0 +1,17 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- wangyuzhe
- yubaihai
- zhaogangtao
labels:
- admin
- admin/main/coupon
- main
options:
no_parent_owners: true
reviewers:
- linmiao
- wangyuzhe
- yubaihai
- zhaogangtao

View File

@@ -0,0 +1,14 @@
# point
# 项目简介
1. 劵管理后台
# 编译环境
go 1.8.4
# 依赖包
# 编译执行

View File

@@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["coupon-admin.toml"],
importpath = "go-common/app/admin/main/coupon/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/coupon/conf:go_default_library",
"//app/admin/main/coupon/http:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log: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,112 @@
# This is a TOML document. Boom
version = "1.0.0"
user = "nobody"
pid = "/tmp/coupon.pid"
dir = "./"
trace = false
debug = false
[log]
dir = "/data/log/coupon"
[bm]
addr = "0.0.0.0:9001"
maxListen = 10
timeout = "1s"
[auth]
managerHost = "http://manager.bilibili.co"
dashboardHost = "http://dashboard-mng.bilibili.co"
dashboardCaller = "manager-go"
[auth.DsHTTPClient]
key = "manager-go"
secret = "949bbb2dd3178252638c2407578bc7ad"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.DsHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.MaHTTPClient]
key = "f6433799dbd88751"
secret = "36f8ddb1806207fe07013ab6a77a3935"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.MaHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.session]
sessionIDLength = 32
cookieLifeTime = 1800
cookieName = "mng-go"
domain = ".bilibili.co"
[auth.session.Memcache]
name = "go-business/auth"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 5
idle = 5
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
[mysql]
addr = "127.0.0.1:3306"
dsn = "root:123456@tcp(127.0.0.1:3306)/bilibili_coupon?timeout=2s&readTimeout=2s&writeTimeout=2s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
readDSN = ["root:123456@tcp(127.0.0.1:3306)/bilibili_coupon?timeout=2s&readTimeout=2s&writeTimeout=2s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "2s"
[mysql.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[rpcClient2]
[rpcClient2.coupon]
[memcache]
name = "coupon"
proto = "tcp"
addr = "172.18.33.60:11237"
idle = 5
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "24h"
[prop]
messageURL="http://message.bilibili.co"
salarySleepTime="10ms"
allowanceTableCount=10
salaryMsgOpen=true
msgSysnSize=10
[HTTPClient]
key = "f022126a8a365e20"
secret = "b7b86838145d634b487e67b811b8fab2"
dial = "30ms"
timeout = "100ms"
keepAlive = "60s"
[HTTPClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100

View File

@@ -0,0 +1,45 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/admin/main/coupon/conf"
"go-common/app/admin/main/coupon/http"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
// init log
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("coupon start")
// ecode init
ecode.Init(conf.Conf.Ecode)
// service init
http.Init(conf.Conf)
// init pprof conf.Conf.Perf
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("point get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("point exit")
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,41 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/admin/main/coupon/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml: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,123 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/memcache"
"go-common/library/conf"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/net/rpc"
"go-common/library/net/rpc/warden"
xtime "go-common/library/time"
"github.com/BurntSushi/toml"
)
// global var
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config config set
type Config struct {
// base
// elk
Log *log.Config
// http
BM *bm.ServerConfig
// MySQL
MySQL *sql.Config
// ecode
Ecode *ecode.Config
//auth
Auth *permit.Config
Prop *Properties
// rpc client
RPCClient2 *RPC
// memcache
Memcache *Memcache
// BroadcastRPC grpc
PGCRPC *warden.ClientConfig
// http client
HTTPClient *bm.ClientConfig
}
// Properties sysconf.
type Properties struct {
MessageURL string
AllowanceTableCount int64
SalarySleepTime xtime.Duration
SalaryMsgOpen bool
SalaryNormalMsgOpen bool
MsgSysnSize int
}
// Memcache memcache
type Memcache struct {
*memcache.Config
Expire xtime.Duration
}
// RPC config
type RPC struct {
Coupon *rpc.ClientConfig
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Value("coupon-admin.toml"); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,68 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"code.go",
"dao.go",
"http.go",
"memcache.go",
"mysql.go",
"remote.go",
],
importpath = "go-common/app/admin/main/coupon/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/coupon/conf:go_default_library",
"//app/admin/main/coupon/model:go_default_library",
"//app/service/openplatform/pgc-season/api/grpc/season/v1:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/elastic:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//vendor/github.com/pkg/errors: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"],
)
go_test(
name = "go_default_test",
srcs = [
"code_test.go",
"dao_test.go",
"http_test.go",
"memcache_test.go",
"mysql_test.go",
"remote_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/coupon/conf:go_default_library",
"//app/admin/main/coupon/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)

View File

@@ -0,0 +1,126 @@
package dao
import (
"bytes"
"context"
"fmt"
"go-common/app/admin/main/coupon/model"
"go-common/library/database/sql"
"github.com/pkg/errors"
)
const (
_codeCountSQL = "SELECT COUNT(1) FROM bilibili_coupon.coupon_code WHERE 1=1 %s"
_codeListSQL = "SELECT id,batch_token,state,code,mid,coupon_token,coupon_type,ver,ctime,mtime FROM coupon_code WHERE 1=1 %s %s"
_codeBlockSQL = "UPDATE coupon_code SET state = ?, ver = ver +1 WHERE id = ? AND ver =?;"
_codeByIDSQL = "SELECT id,batch_token,state,code,mid,coupon_token,coupon_type,ver,ctime,mtime FROM coupon_code WHERE id = ?"
_batchAddCodeSQL = "INSERT IGNORE INTO coupon_code(batch_token,state,code,coupon_type)VALUES "
)
// CountCode coupon code count.
func (d *Dao) CountCode(c context.Context, a *model.ArgCouponCode) (count int64, err error) {
sql := fmt.Sprintf(_codeCountSQL, whereSQL(a))
if err = d.db.QueryRow(c, sql).Scan(&count); err != nil {
err = errors.Wrapf(err, "dao code list")
}
return
}
// CodeList code list.
func (d *Dao) CodeList(c context.Context, a *model.ArgCouponCode) (res []*model.CouponCode, err error) {
listSQL := fmt.Sprintf(_codeListSQL, whereSQL(a), pageSQL(a.Pn, a.Ps))
var rows *sql.Rows
if rows, err = d.db.Query(c, listSQL); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.CouponCode)
if err = rows.Scan(&r.ID, &r.BatchToken, &r.State, &r.Code, &r.Mid, &r.CouponToken, &r.CouponType, &r.Ver, &r.Ctime, &r.Mtime); err != nil {
err = errors.WithStack(err)
res = nil
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// UpdateCodeBlock update code block.
func (d *Dao) UpdateCodeBlock(c context.Context, a *model.CouponCode) (err error) {
if _, err = d.db.Exec(c, _codeBlockSQL, a.State, a.ID, a.Ver); err != nil {
err = errors.Wrapf(err, "dao update code block(%+v)", a)
}
return
}
// CodeByID code by id.
func (d *Dao) CodeByID(c context.Context, id int64) (r *model.CouponCode, err error) {
r = new(model.CouponCode)
if err = d.db.QueryRow(c, _codeByIDSQL, id).
Scan(&r.ID, &r.BatchToken, &r.State, &r.Code, &r.Mid, &r.CouponToken, &r.CouponType, &r.Ver, &r.Ctime, &r.Mtime); err != nil {
if err == sql.ErrNoRows {
r = nil
err = nil
return
}
err = errors.Wrapf(err, "dao query code by id")
}
return
}
// BatchAddCode batch add code.
func (d *Dao) BatchAddCode(c context.Context, cs []*model.CouponCode) (err error) {
var (
buf bytes.Buffer
sql string
)
buf.WriteString(_batchAddCodeSQL)
for _, v := range cs {
buf.WriteString("('")
buf.WriteString(v.BatchToken)
buf.WriteString("',")
buf.WriteString(fmt.Sprintf("%d", v.State))
buf.WriteString(",'")
buf.WriteString(v.Code)
buf.WriteString("',")
buf.WriteString(fmt.Sprintf("%d", v.CouponType))
buf.WriteString("),")
}
sql = buf.String()
if _, err = d.db.Exec(c, sql[0:len(sql)-1]); err != nil {
err = errors.Wrapf(err, "dao insert codes")
}
return
}
func whereSQL(a *model.ArgCouponCode) (sql string) {
if a == nil {
return
}
if a.Mid > 0 {
sql += " AND mid = " + fmt.Sprintf("%d", a.Mid)
}
if a.Code != "" {
sql += " AND code = '" + a.Code + "'"
}
if a.BatchToken != "" {
sql += " AND batch_token = '" + a.BatchToken + "'"
}
return sql
}
func pageSQL(pn, ps int) (sql string) {
if pn <= 0 {
pn = 1
}
if ps <= 0 {
ps = 20
}
return " ORDER BY ID DESC LIMIT " + fmt.Sprintf("%d,%d", (pn-1)*ps, ps)
}

View File

@@ -0,0 +1,125 @@
package dao
import (
"context"
"fmt"
"testing"
"time"
"go-common/app/admin/main/coupon/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoCountCode(t *testing.T) {
convey.Convey("CountCode", t, func(convCtx convey.C) {
var (
c = context.Background()
a = &model.ArgCouponCode{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
count, err := d.CountCode(c, a)
convCtx.Convey("Then err should be nil.count should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(count, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoCodeList(t *testing.T) {
convey.Convey("CodeList", t, func(convCtx convey.C) {
var (
c = context.Background()
a = &model.ArgCouponCode{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.CodeList(c, a)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateCodeBlock(t *testing.T) {
convey.Convey("UpdateCodeBlock", t, func(convCtx convey.C) {
var (
c = context.Background()
a = &model.CouponCode{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.UpdateCodeBlock(c, a)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoCodeByID(t *testing.T) {
convey.Convey("CodeByID", t, func(convCtx convey.C) {
var (
c = context.Background()
id = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
_, err := d.CodeByID(c, id)
convCtx.Convey("Then err should be nil.r should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoBatchAddCode(t *testing.T) {
convey.Convey("BatchAddCode", t, func(convCtx convey.C) {
var (
c = context.Background()
cs = []*model.CouponCode{
{
BatchToken: fmt.Sprintf("%d", time.Now().Unix()),
Code: fmt.Sprintf("%d", time.Now().Unix()),
CouponType: 3,
State: model.CodeStateNotUse,
},
}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.BatchAddCode(c, cs)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaowhereSQL(t *testing.T) {
convey.Convey("whereSQL", t, func(convCtx convey.C) {
var (
a = &model.ArgCouponCode{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
sql := whereSQL(a)
convCtx.Convey("Then sql should not be nil.", func(convCtx convey.C) {
convCtx.So(sql, convey.ShouldNotBeNil)
})
})
})
}
func TestDaopageSQL(t *testing.T) {
convey.Convey("pageSQL", t, func(convCtx convey.C) {
var (
pn = int(0)
ps = int(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
sql := pageSQL(pn, ps)
convCtx.Convey("Then sql should not be nil.", func(convCtx convey.C) {
convCtx.So(sql, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,51 @@
package dao
import (
"context"
"go-common/app/admin/main/coupon/conf"
seasongrpc "go-common/app/service/openplatform/pgc-season/api/grpc/season/v1"
"go-common/library/cache/memcache"
"go-common/library/database/elastic"
xsql "go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
// Dao dao
type Dao struct {
c *conf.Config
db *xsql.DB
es *elastic.Elastic
mc *memcache.Pool
// grpc
rpcClient seasongrpc.SeasonClient
client *bm.Client
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
db: xsql.NewMySQL(c.MySQL),
// es
es: elastic.NewElastic(nil),
mc: memcache.NewPool(c.Memcache.Config),
client: bm.NewClient(c.HTTPClient),
}
var err error
if dao.rpcClient, err = seasongrpc.NewClient(c.PGCRPC); err != nil {
log.Error("seasongrpc NewClientt error(%v)", err)
}
return
}
// Close close the resource.
func (dao *Dao) Close() {
dao.db.Close()
}
// Ping dao ping
func (dao *Dao) Ping(c context.Context) error {
return dao.db.Ping(c)
}

View File

@@ -0,0 +1,47 @@
package dao
import (
"flag"
"os"
"strings"
"testing"
"go-common/app/admin/main/coupon/conf"
gock "gopkg.in/h2non/gock.v1"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account.coupon-admin")
flag.Set("conf_appid", "main.account.coupon-admin")
flag.Set("conf_token", "9b7cd88bef452958c66efec00a334d4d")
flag.Set("tree_id", "38771")
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/coupon-admin.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
d.client.SetTransport(gock.DefaultTransport)
os.Exit(m.Run())
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}

View File

@@ -0,0 +1,32 @@
package dao
import (
"context"
"net/url"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
couponSystemNotify = "4"
couponMC = "10_99_1"
sendMessage = "/api/notify/send.user.notify.do"
)
//SendMessage send message.
func (d *Dao) SendMessage(c context.Context, mids, title, content, ip string) (err error) {
params := url.Values{}
params.Set("mc", couponMC)
params.Set("title", title)
params.Set("context", content)
params.Set("data_type", couponSystemNotify)
params.Set("mid_list", mids)
if err = d.client.Post(c, d.c.Prop.MessageURL+sendMessage, ip, params, nil); err != nil {
err = errors.WithStack(err)
}
log.Info("send message url:%+v params:%+v err:%+v", d.c.Prop.MessageURL+sendMessage, params, err)
return
}

View File

@@ -0,0 +1,29 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
gock "gopkg.in/h2non/gock.v1"
)
func TestDaoSendMessage(t *testing.T) {
convey.Convey("SendMessage", t, func(convCtx convey.C) {
var (
c = context.Background()
mids = ""
title = ""
content = ""
ip = ""
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
defer gock.OffAll()
httpMock("POST", sendMessage).Reply(200).JSON(`{"code":0}`)
err := d.SendMessage(c, mids, title, content, ip)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,81 @@
package dao
import (
"context"
"fmt"
gmc "go-common/library/cache/memcache"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
_prefixCouponAllowances = "cas:%d:%d"
_prefixCoupons = "cs:%d:%d"
_prefixGrantUnique = "gu:%s"
)
func couponAllowancesKey(mid int64, state int8) string {
return fmt.Sprintf(_prefixCouponAllowances, mid, state)
}
func couponsKey(mid int64, ct int8) string {
return fmt.Sprintf(_prefixCoupons, ct, mid)
}
func grantUnique(token string) string {
return fmt.Sprintf(_prefixGrantUnique, token)
}
// DelCouponAllowancesKey delete allowances cache.
func (d *Dao) DelCouponAllowancesKey(c context.Context, mid int64, state int8) (err error) {
return d.delCache(c, couponAllowancesKey(mid, state))
}
// DelCache del cache.
func (d *Dao) delCache(c context.Context, key string) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Delete(key); err != nil {
if err == gmc.ErrNotFound {
err = nil
} else {
err = errors.Wrapf(err, "mc.Delete(%s)", key)
}
}
return
}
//DelCouponTypeCache del coupon.
func (d *Dao) DelCouponTypeCache(c context.Context, mid int64, ct int8) (err error) {
return d.delCache(c, couponsKey(mid, ct))
}
//DelGrantUniqueLock del lock.
func (d *Dao) DelGrantUniqueLock(c context.Context, token string) (err error) {
return d.delCache(c, grantUnique(token))
}
// AddGrantUniqueLock add grant coupon use lock.
func (d *Dao) AddGrantUniqueLock(c context.Context, token string, seconds int32) (succeed bool) {
var (
key = grantUnique(token)
conn = d.mc.Get(c)
err error
)
defer conn.Close()
item := &gmc.Item{
Key: key,
Value: []byte("0"),
Expiration: seconds,
}
if err = conn.Add(item); err != nil {
if err != gmc.ErrNotStored {
log.Error("mc.Add(%s) error(%v)", key, err)
}
return
}
succeed = true
return
}

View File

@@ -0,0 +1,128 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaocouponAllowancesKey(t *testing.T) {
convey.Convey("couponAllowancesKey", t, func(convCtx convey.C) {
var (
mid = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := couponAllowancesKey(mid, 0)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaocouponsKey(t *testing.T) {
convey.Convey("couponsKey", t, func(convCtx convey.C) {
var (
mid = int64(0)
ct = int8(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := couponsKey(mid, ct)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaograntUnique(t *testing.T) {
convey.Convey("grantUnique", t, func(convCtx convey.C) {
var (
token = ""
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := grantUnique(token)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoDelCouponAllowancesKey(t *testing.T) {
convey.Convey("DelCouponAllowancesKey", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.DelCouponAllowancesKey(c, mid, 0)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaodelCache(t *testing.T) {
convey.Convey("delCache", t, func(convCtx convey.C) {
var (
c = context.Background()
key = "123"
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.delCache(c, key)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelCouponTypeCache(t *testing.T) {
convey.Convey("DelCouponTypeCache", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(0)
ct = int8(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.DelCouponTypeCache(c, mid, ct)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelGrantUniqueLock(t *testing.T) {
convey.Convey("DelGrantUniqueLock", t, func(convCtx convey.C) {
var (
c = context.Background()
token = ""
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.DelGrantUniqueLock(c, token)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddGrantUniqueLock(t *testing.T) {
convey.Convey("AddGrantUniqueLock", t, func(convCtx convey.C) {
var (
c = context.Background()
token = ""
seconds = int32(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
succeed := d.AddGrantUniqueLock(c, token, seconds)
convCtx.Convey("Then succeed should not be nil.", func(convCtx convey.C) {
convCtx.So(succeed, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,481 @@
package dao
import (
"bytes"
"context"
xsql "database/sql"
"fmt"
"strconv"
"strings"
"go-common/app/admin/main/coupon/model"
"go-common/library/database/sql"
"github.com/pkg/errors"
)
const (
_addbatch = "INSERT INTO coupon_batch_info(app_id,name,batch_token,max_count,current_count,start_time,expire_time,ver,ctime,limit_count,operator)VALUES(?,?,?,?,?,?,?,?,?,?,?);"
_addAllowancebatch = "INSERT INTO coupon_batch_info(app_id,name,batch_token,max_count,current_count,start_time,expire_time,expire_day,ver,ctime,limit_count,operator,full_amount,amount,state,coupon_type,platform_limit,product_limit_month,product_limit_renewal)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"
_batchList = "SELECT id,app_id,name,batch_token,max_count,current_count,start_time,expire_time,expire_day,ver,ctime,mtime,limit_count,operator,full_amount,amount,state,coupon_type,platform_limit,product_limit_month,product_limit_renewal FROM coupon_batch_info WHERE 1=1 "
_appAppInfoSQL = "SELECT id,name,app_key,notify_url,ctime,mtime FROM coupon_app_info;"
_updateAllowanceBatchSQL = "UPDATE coupon_batch_info SET app_id = ?,name = ?,max_count = ?,limit_count = ?,operator = ?,platform_limit = ?,product_limit_month = ?,product_limit_renewal = ? WHERE id = ?;"
_updateBatchStatusSQL = "UPDATE coupon_batch_info SET state = ?, operator = ? WHERE id = ?;"
_batchInfoSQL = "SELECT id,app_id,name,batch_token,max_count,current_count,start_time,expire_time,expire_day,ver,ctime,mtime,limit_count,operator,full_amount,amount,state,coupon_type,platform_limit,product_limit_month,product_limit_renewal FROM coupon_batch_info WHERE batch_token = ? "
_batchInfoByIDSQL = "SELECT id,app_id,name,batch_token,max_count,current_count,start_time,expire_time,expire_day,ver,ctime,mtime,limit_count,operator,full_amount,amount,state,coupon_type,platform_limit,product_limit_month,product_limit_renewal FROM coupon_batch_info WHERE id = ? "
_updateAllowanceStateSQL = "UPDATE coupon_allowance_info_%02d SET state = ?,ver = ver+1 WHERE coupon_token = ? AND ver = ?;"
_couponAllowanceByTokenSQL = "SELECT id,coupon_token,mid,state,start_time,expire_time,origin,ver,batch_token,order_no,amount,full_amount,ctime,mtime FROM coupon_allowance_info_%02d WHERE coupon_token = ?;"
_addCouponAllowanceChangeLogSQL = "INSERT INTO coupon_allowance_change_log_%02d (coupon_token,order_no,mid,state,ctime, change_type) VALUES(?,?,?,?,?,?);"
_couponAllowancePageSQL = "SELECT id,coupon_token,mid,state,start_time,expire_time,origin,ver,batch_token,order_no,amount,full_amount,ctime,mtime,remark FROM coupon_allowance_info_%02d WHERE mid = ? %s ORDER BY id DESC"
_batchAddAllowanceCouponSQL = "INSERT INTO coupon_allowance_info_%02d(coupon_token,mid,state,start_time,expire_time,origin,batch_token,amount,full_amount,ctime,app_id) VALUES "
_updateBatchSQL = "UPDATE coupon_batch_info SET current_count = current_count + ? WHERE batch_token = ?;"
_updateCodeBatchSQL = "UPDATE coupon_batch_info SET app_id = ?,name = ?,limit_count = ?,operator = ?,platform_limit = ?,product_limit_month = ?,product_limit_renewal = ? WHERE id = ?;"
//view
_addViewBatchSQL = "INSERT INTO coupon_batch_info(app_id,name,batch_token,max_count,current_count,start_time,expire_time,ver,limit_count,coupon_type,operator)VALUES(?,?,?,?,?,?,?,?,?,?,?)"
_updateViewBatchSQL = "UPDATE coupon_batch_info SET app_id=?,name=?,max_count=?,limit_count=?,operator = ?,ver=? WHERE id=?"
_updateViewSQL = "UPDATE coupon_info_%02d SET state=? WHERE coupon_token=?"
_searchViewSQL = "SELECT coupon_token,mid,batch_token,state,order_no,oid,start_time,expire_time,ctime,mtime FROM coupon_info_%02d WHERE 1=1"
_searchViewCountSQL = "SELECT COUNT(1) FROM coupon_info_%02d WHERE 1=1"
_viewInfoSQL = "SELECT coupon_token,mid,batch_token,state,order_no,oid,start_time,expire_time,ctime FROM coupon_info_%02d WHERE coupon_token=?"
_addViewChangeLog = "INSERT INTO coupon_change_log_%02d(coupon_token,mid,state) VALUES(?,?,?)"
)
// BeginTran begin transaction.
func (d *Dao) BeginTran(c context.Context) (*sql.Tx, error) {
return d.db.Begin(c)
}
func hitAllowanceInfo(mid int64) int64 {
return mid % 10
}
func hitAllowanceChangeLog(mid int64) int64 {
return mid % 10
}
func hitViewInfo(mid int64) int64 {
return mid % 100
}
// BatchList query batch list.
func (d *Dao) BatchList(c context.Context, appid int64, t int8) (res []*model.CouponBatchInfo, err error) {
var (
rows *sql.Rows
sql = _batchList
)
if appid != 0 {
sql += fmt.Sprintf(" AND `app_id` = %d", appid)
}
if t != 0 {
sql += fmt.Sprintf(" AND `coupon_type` = %d", t)
}
if rows, err = d.db.Query(c, sql); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := &model.CouponBatchInfo{}
if err = rows.Scan(&r.ID, &r.AppID, &r.Name, &r.BatchToken, &r.MaxCount, &r.CurrentCount, &r.StartTime, &r.ExpireTime, &r.ExpireDay, &r.Ver,
&r.Ctime, &r.Mtime, &r.LimitCount, &r.Operator, &r.FullAmount, &r.Amount, &r.State, &r.CouponType, &r.PlatformLimit, &r.ProdLimMonth, &r.ProdLimRenewal); err != nil {
err = errors.WithStack(err)
res = nil
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// BatchViewList query batch list.
func (d *Dao) BatchViewList(c context.Context, appid int64, batchToken string, t int8) (res []*model.CouponBatchInfo, err error) {
var (
rows *sql.Rows
sql = _batchList
)
if appid != 0 {
sql += fmt.Sprintf(" AND app_id = %d", appid)
}
if t != 0 {
sql += fmt.Sprintf(" AND coupon_type = %d", t)
}
if len(batchToken) > 0 {
sql += fmt.Sprintf(" AND batch_token='%v'", batchToken)
}
if rows, err = d.db.Query(c, sql); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := &model.CouponBatchInfo{}
if err = rows.Scan(&r.ID, &r.AppID, &r.Name, &r.BatchToken, &r.MaxCount, &r.CurrentCount, &r.StartTime, &r.ExpireTime, &r.ExpireDay, &r.Ver,
&r.Ctime, &r.Mtime, &r.LimitCount, &r.Operator, &r.FullAmount, &r.Amount, &r.State, &r.CouponType, &r.PlatformLimit, &r.ProdLimMonth, &r.ProdLimRenewal); err != nil {
err = errors.WithStack(err)
res = nil
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// AddBatchInfo add batch info.
func (d *Dao) AddBatchInfo(c context.Context, b *model.CouponBatchInfo) (a int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _addbatch, b.AppID, b.Name, b.BatchToken, b.MaxCount, b.CurrentCount, b.StartTime, b.ExpireTime,
b.Ver, b.Ctime, b.LimitCount, b.Operator); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
}
return
}
// AllAppInfo all app info.
func (d *Dao) AllAppInfo(c context.Context) (res []*model.AppInfo, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(c, _appAppInfoSQL); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := &model.AppInfo{}
if err = rows.Scan(&r.ID, &r.Name, &r.Appkey, &r.NotifyURL, &r.Ctime, &r.Mtime); err != nil {
err = errors.WithStack(err)
res = nil
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// AddAllowanceBatchInfo add allowance batch info.
func (d *Dao) AddAllowanceBatchInfo(c context.Context, b *model.CouponBatchInfo) (a int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _addAllowancebatch, b.AppID, b.Name, b.BatchToken, b.MaxCount, b.CurrentCount, b.StartTime, b.ExpireTime, b.ExpireDay,
b.Ver, b.Ctime, b.LimitCount, b.Operator, b.FullAmount, b.Amount, b.State, b.CouponType, b.PlatformLimit, b.ProdLimMonth, b.ProdLimRenewal); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
}
return
}
// UpdateAllowanceBatchInfo update allowance batch info.
func (d *Dao) UpdateAllowanceBatchInfo(c context.Context, b *model.CouponBatchInfo) (a int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _updateAllowanceBatchSQL, b.AppID, b.Name, b.MaxCount, b.LimitCount, b.Operator, b.PlatformLimit, b.ProdLimMonth, b.ProdLimRenewal, b.ID); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
}
return
}
// UpdateCodeBatchInfo update code batch info.
func (d *Dao) UpdateCodeBatchInfo(c context.Context, b *model.CouponBatchInfo) (a int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _updateCodeBatchSQL, b.AppID, b.Name, b.LimitCount, b.Operator, b.PlatformLimit, b.ProdLimMonth, b.ProdLimRenewal, b.ID); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
}
return
}
// UpdateBatchStatus update batch status.
func (d *Dao) UpdateBatchStatus(c context.Context, status int8, operator string, id int64) (a int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _updateBatchStatusSQL, status, operator, id); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
}
return
}
//BatchInfo batch info.
func (d *Dao) BatchInfo(c context.Context, token string) (r *model.CouponBatchInfo, err error) {
var row *sql.Row
r = new(model.CouponBatchInfo)
row = d.db.Master().QueryRow(c, _batchInfoSQL, token)
if err = row.Scan(&r.ID, &r.AppID, &r.Name, &r.BatchToken, &r.MaxCount, &r.CurrentCount, &r.StartTime, &r.ExpireTime, &r.ExpireDay, &r.Ver,
&r.Ctime, &r.Mtime, &r.LimitCount, &r.Operator, &r.FullAmount, &r.Amount, &r.State, &r.CouponType, &r.PlatformLimit, &r.ProdLimMonth, &r.ProdLimRenewal); err != nil {
if err == sql.ErrNoRows {
err = nil
r = nil
return
}
err = errors.WithStack(err)
return
}
return
}
//BatchInfoByID batch info by id.
func (d *Dao) BatchInfoByID(c context.Context, id int64) (r *model.CouponBatchInfo, err error) {
var row *sql.Row
r = new(model.CouponBatchInfo)
row = d.db.QueryRow(c, _batchInfoByIDSQL, id)
if err = row.Scan(&r.ID, &r.AppID, &r.Name, &r.BatchToken, &r.MaxCount, &r.CurrentCount, &r.StartTime, &r.ExpireTime, &r.ExpireDay, &r.Ver,
&r.Ctime, &r.Mtime, &r.LimitCount, &r.Operator, &r.FullAmount, &r.Amount, &r.State, &r.CouponType, &r.PlatformLimit, &r.ProdLimMonth, &r.ProdLimRenewal); err != nil {
if err == sql.ErrNoRows {
err = nil
r = nil
return
}
err = errors.WithStack(err)
return
}
return
}
// UpdateAllowanceStatus update allowance status.
func (d *Dao) UpdateAllowanceStatus(c context.Context, tx *sql.Tx, state int8, mid int64, token string, ver int64) (a int64, err error) {
var res xsql.Result
if res, err = tx.Exec(fmt.Sprintf(_updateAllowanceStateSQL, hitAllowanceInfo(mid)), state, token, ver); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
}
return
}
// AllowanceByToken query coupon by token.
func (d *Dao) AllowanceByToken(c context.Context, mid int64, token string) (r *model.CouponAllowanceInfo, err error) {
var row *sql.Row
r = &model.CouponAllowanceInfo{}
row = d.db.QueryRow(c, fmt.Sprintf(_couponAllowanceByTokenSQL, hitAllowanceInfo(mid)), token)
if err = row.Scan(&r.ID, &r.CouponToken, &r.Mid, &r.State, &r.StartTime, &r.ExpireTime, &r.Origin, &r.Ver, &r.BatchToken,
&r.OrderNO, &r.Amount, &r.FullAmount, &r.CTime, &r.MTime); err != nil {
if err == sql.ErrNoRows {
err = nil
r = nil
return
}
err = errors.WithStack(err)
return
}
return
}
//InsertCouponAllowanceHistory insert coupon history .
func (d *Dao) InsertCouponAllowanceHistory(c context.Context, tx *sql.Tx, l *model.CouponAllowanceChangeLog) (a int64, err error) {
var res xsql.Result
if res, err = tx.Exec(fmt.Sprintf(_addCouponAllowanceChangeLogSQL, hitAllowanceChangeLog(l.Mid)), l.CouponToken, l.OrderNO, l.Mid, l.State, l.Ctime, l.ChangeType); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
}
return
}
//AllowanceList allowance list.
func (d *Dao) AllowanceList(c context.Context, arg *model.ArgAllowanceSearch) (res []*model.CouponAllowanceInfo, err error) {
var (
rows *sql.Rows
whereSQL = " "
)
if arg.AppID != 0 {
whereSQL += fmt.Sprintf(" AND `app_id` = %d ", arg.AppID)
}
if arg.CouponToken != "" {
whereSQL += fmt.Sprintf(" AND `coupon_token` = '%s' ", arg.CouponToken)
}
if arg.OrderNO != "" {
whereSQL += fmt.Sprintf(" AND `order_no` = '%s' ", arg.OrderNO)
}
if arg.BatchToken != "" {
whereSQL += fmt.Sprintf(" AND `batch_token` = '%s' ", arg.BatchToken)
}
if rows, err = d.db.Query(c, fmt.Sprintf(_couponAllowancePageSQL, hitAllowanceInfo(arg.Mid), whereSQL), arg.Mid); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.CouponAllowanceInfo)
if err = rows.Scan(&r.ID, &r.CouponToken, &r.Mid, &r.State, &r.StartTime, &r.ExpireTime, &r.Origin, &r.Ver, &r.BatchToken, &r.OrderNO, &r.Amount, &r.FullAmount,
&r.CTime, &r.MTime, &r.Remark); err != nil {
err = errors.WithStack(err)
res = nil
return
}
res = append(res, r)
}
err = rows.Err()
return
}
//AddViewBatch add view batch.
func (d *Dao) AddViewBatch(c context.Context, arg *model.ArgCouponViewBatch) (err error) {
if _, err = d.db.Exec(c, _addViewBatchSQL, arg.AppID, arg.Name, arg.BatchToken, arg.MaxCount, arg.CurrentCount, arg.StartTime, arg.ExpireTime, arg.Ver, arg.LimitCount, arg.CouponType, arg.Operator); err != nil {
err = errors.WithStack(err)
}
return
}
//UpdateViewBatch update viewBatch
func (d *Dao) UpdateViewBatch(c context.Context, arg *model.ArgCouponViewBatch) (err error) {
if _, err = d.db.Exec(c, _updateViewBatchSQL, arg.AppID, arg.Name, arg.MaxCount, arg.LimitCount, arg.Operator, arg.Ver, arg.ID); err != nil {
err = errors.WithStack(err)
}
return
}
//TxUpdateViewInfo update view info.
func (d *Dao) TxUpdateViewInfo(tx *sql.Tx, status int8, couponToken string, mid int64) (err error) {
if _, err = tx.Exec(fmt.Sprintf(_updateViewSQL, hitViewInfo(mid)), status, couponToken); err != nil {
err = errors.WithStack(err)
}
return
}
//TxCouponViewLog tx add view log.
func (d *Dao) TxCouponViewLog(tx *sql.Tx, arg *model.CouponChangeLog) (err error) {
if _, err = tx.Exec(fmt.Sprintf(_addViewChangeLog, hitViewInfo(arg.Mid)), arg.CouponToken, arg.Mid, arg.State); err != nil {
err = errors.WithStack(err)
}
return
}
//CouponViewInfo .
func (d *Dao) CouponViewInfo(c context.Context, couponToken string, mid int64) (r *model.CouponInfo, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_viewInfoSQL, hitViewInfo(mid)), couponToken)
r = new(model.CouponInfo)
if err = row.Scan(&r.CouponToken, &r.Mid, &r.BatchToken, &r.State, &r.OrderNo, &r.OID, &r.StartTime, &r.ExpireTime, &r.Ctime); err != nil {
err = errors.WithStack(err)
}
return
}
//SearchViewCouponCount search view count.
func (d *Dao) SearchViewCouponCount(c context.Context, arg *model.ArgSearchCouponView) (count int64, err error) {
whereSQL := fmt.Sprintf(_searchViewCountSQL, hitViewInfo(arg.Mid))
whereSQL += fmt.Sprintf(" AND mid=%v", arg.Mid)
if len(arg.CouponToken) > 0 {
whereSQL += fmt.Sprintf(" AND coupon_token='%v'", arg.CouponToken)
}
if len(arg.BatchTokens) > 0 {
whereSQL += fmt.Sprintf(" AND batch_token IN('%v')", strings.Join(arg.BatchTokens, "','"))
}
row := d.db.QueryRow(c, whereSQL)
if err = row.Scan(&count); err != nil {
err = errors.WithStack(err)
}
return
}
//SearchViewCouponInfo search view coupon info .
func (d *Dao) SearchViewCouponInfo(c context.Context, arg *model.ArgSearchCouponView) (res []*model.CouponInfo, err error) {
var rows *sql.Rows
whereSQL := fmt.Sprintf(_searchViewSQL, hitViewInfo(arg.Mid))
whereSQL += fmt.Sprintf(" AND mid=%v", arg.Mid)
if len(arg.CouponToken) > 0 {
whereSQL += fmt.Sprintf(" AND coupon_token='%v'", arg.CouponToken)
}
if len(arg.BatchTokens) > 0 {
whereSQL += fmt.Sprintf(" AND batch_token IN('%v')", strings.Join(arg.BatchTokens, "','"))
}
if arg.PN <= 0 {
arg.PN = 1
}
if arg.PS <= 0 || arg.PS >= 100 {
arg.PS = 20
}
whereSQL += fmt.Sprintf(" ORDER BY ID DESC LIMIT %v,%v", (arg.PN-1)*arg.PS, arg.PS)
if rows, err = d.db.Query(c, whereSQL); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.CouponInfo)
if err = rows.Scan(&r.CouponToken, &r.Mid, &r.BatchToken, &r.State, &r.OrderNo, &r.OID, &r.StartTime, &r.ExpireTime, &r.Ctime, &r.Mtime); err != nil {
err = errors.WithStack(err)
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// BatchAddAllowanceCoupon batch add allowance coupon.
func (d *Dao) BatchAddAllowanceCoupon(c context.Context, tx *sql.Tx, cps []*model.CouponAllowanceInfo) (a int64, err error) {
var (
buf bytes.Buffer
res xsql.Result
sql string
)
buf.WriteString(fmt.Sprintf(_batchAddAllowanceCouponSQL, hitAllowanceInfo(cps[0].Mid)))
for _, v := range cps {
buf.WriteString("('")
buf.WriteString(v.CouponToken)
buf.WriteString("',")
buf.WriteString(strconv.FormatInt(v.Mid, 10))
buf.WriteString(",")
buf.WriteString(fmt.Sprintf("%d", v.State))
buf.WriteString(",")
buf.WriteString(strconv.FormatInt(v.StartTime, 10))
buf.WriteString(",")
buf.WriteString(strconv.FormatInt(v.ExpireTime, 10))
buf.WriteString(",")
buf.WriteString(fmt.Sprintf("%d", v.Origin))
buf.WriteString(",'")
buf.WriteString(v.BatchToken)
buf.WriteString("',")
buf.WriteString(fmt.Sprintf("%f", v.Amount))
buf.WriteString(",")
buf.WriteString(fmt.Sprintf("%f", v.FullAmount))
buf.WriteString(",'")
buf.WriteString(v.CTime.Time().Format("2006-01-02 15:04:05"))
buf.WriteString("',")
buf.WriteString(strconv.FormatInt(v.AppID, 10))
buf.WriteString("),")
}
sql = buf.String()
if res, err = tx.Exec(sql[0 : len(sql)-1]); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
}
return
}
// UpdateBatchInfo update batch info.
func (d *Dao) UpdateBatchInfo(c context.Context, tx *sql.Tx, token string, count int) (a int64, err error) {
var res xsql.Result
if res, err = tx.Exec(_updateBatchSQL, count, token); err != nil {
err = errors.WithStack(err)
return
}
if a, err = res.RowsAffected(); err != nil {
err = errors.WithStack(err)
return
}
return
}

View File

@@ -0,0 +1,484 @@
package dao
import (
"bytes"
"context"
"fmt"
"math/rand"
"time"
// xsql"database/sql"
"go-common/app/admin/main/coupon/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoBeginTran(t *testing.T) {
convey.Convey("BeginTran", t, func(convCtx convey.C) {
var (
c = context.Background()
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1, err := d.BeginTran(c)
convCtx.Convey("Then err should be nil.p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaohitAllowanceInfo(t *testing.T) {
convey.Convey("hitAllowanceInfo", t, func(convCtx convey.C) {
var (
mid = int64(1)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := hitAllowanceInfo(mid)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaohitAllowanceChangeLog(t *testing.T) {
convey.Convey("hitAllowanceChangeLog", t, func(convCtx convey.C) {
var (
mid = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := hitAllowanceChangeLog(mid)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaohitViewInfo(t *testing.T) {
convey.Convey("hitViewInfo", t, func(convCtx convey.C) {
var (
mid = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := hitViewInfo(mid)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
// go test -test.v -test.run TestBatchList
func TestDaoBatchList(t *testing.T) {
convey.Convey("BatchList", t, func(convCtx convey.C) {
var (
c = context.Background()
appid = int64(1)
t = int8(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.BatchList(c, appid, t)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoBatchViewList(t *testing.T) {
convey.Convey("BatchViewList", t, func(convCtx convey.C) {
var (
c = context.Background()
appid = int64(0)
batchToken = ""
no = int8(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.BatchViewList(c, appid, batchToken, no)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddBatchInfo(t *testing.T) {
convey.Convey("AddBatchInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
bi = &model.CouponBatchInfo{}
b bytes.Buffer
)
b.WriteString(fmt.Sprintf("%07d", rand.Int63n(9999999)))
b.WriteString(fmt.Sprintf("%03d", time.Now().UnixNano()/1e6%1000))
b.WriteString(time.Now().Format("20060102150405"))
bi.BatchToken = b.String()
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
a, err := d.AddBatchInfo(c, bi)
convCtx.Convey("Then err should be nil.a should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(a, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAllAppInfo(t *testing.T) {
convey.Convey("AllAppInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.AllAppInfo(c)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddAllowanceBatchInfo(t *testing.T) {
convey.Convey("AddAllowanceBatchInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
b bytes.Buffer
bi = &model.CouponBatchInfo{}
)
b.WriteString(fmt.Sprintf("%07d", rand.Int63n(9999999)))
b.WriteString(fmt.Sprintf("%03d", time.Now().UnixNano()/1e6%1000))
b.WriteString(time.Now().Format("20060102150405"))
bi.BatchToken = b.String()
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
a, err := d.AddAllowanceBatchInfo(c, bi)
convCtx.Convey("Then err should be nil.a should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(a, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateAllowanceBatchInfo(t *testing.T) {
convey.Convey("UpdateAllowanceBatchInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
b = &model.CouponBatchInfo{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
a, err := d.UpdateAllowanceBatchInfo(c, b)
convCtx.Convey("Then err should be nil.a should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(a, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateBatchStatus(t *testing.T) {
convey.Convey("UpdateBatchStatus", t, func(convCtx convey.C) {
var (
c = context.Background()
status = int8(0)
operator = ""
id = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
a, err := d.UpdateBatchStatus(c, status, operator, id)
convCtx.Convey("Then err should be nil.a should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(a, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoBatchInfo(t *testing.T) {
convey.Convey("BatchInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
token = ""
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
r, err := d.BatchInfo(c, token)
convCtx.Convey("Then err should be nil.r should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(r, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateAllowanceStatus(t *testing.T) {
convey.Convey("UpdateAllowanceStatus", t, func(convCtx convey.C) {
var (
c = context.Background()
tx, _ = d.BeginTran(context.Background())
state = int8(0)
mid = int64(0)
token = ""
ver = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
a, err := d.UpdateAllowanceStatus(c, tx, state, mid, token, ver)
if err == nil {
if err = tx.Commit(); err != nil {
tx.Rollback()
}
} else {
tx.Rollback()
}
convCtx.Convey("Then err should be nil.a should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(a, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAllowanceByToken(t *testing.T) {
convey.Convey("AllowanceByToken", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(13)
token = "000000119720180929180009"
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
r, err := d.AllowanceByToken(c, mid, token)
convCtx.Convey("Then err should be nil.r should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(r, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoInsertCouponAllowanceHistory(t *testing.T) {
convey.Convey("InsertCouponAllowanceHistory", t, func(convCtx convey.C) {
var (
c = context.Background()
tx, _ = d.BeginTran(context.Background())
l = &model.CouponAllowanceChangeLog{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
a, err := d.InsertCouponAllowanceHistory(c, tx, l)
if err == nil {
if err = tx.Commit(); err != nil {
tx.Rollback()
}
} else {
tx.Rollback()
}
convCtx.Convey("Then err should be nil.a should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(a, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAllowanceList(t *testing.T) {
convey.Convey("AllowanceList", t, func(convCtx convey.C) {
var (
c = context.Background()
arg = &model.ArgAllowanceSearch{Mid: 3}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.AllowanceList(c, arg)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddViewBatch(t *testing.T) {
convey.Convey("AddViewBatch", t, func(convCtx convey.C) {
var (
c = context.Background()
arg = &model.ArgCouponViewBatch{}
b bytes.Buffer
)
b.WriteString(fmt.Sprintf("%07d", rand.Int63n(9999999)))
b.WriteString(fmt.Sprintf("%03d", time.Now().UnixNano()/1e6%1000))
b.WriteString(time.Now().Format("20060102150405"))
arg.BatchToken = b.String()
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.AddViewBatch(c, arg)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoUpdateViewBatch(t *testing.T) {
convey.Convey("UpdateViewBatch", t, func(convCtx convey.C) {
var (
c = context.Background()
arg = &model.ArgCouponViewBatch{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.UpdateViewBatch(c, arg)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxUpdateViewInfo(t *testing.T) {
convey.Convey("TxUpdateViewInfo", t, func(convCtx convey.C) {
var (
tx, _ = d.BeginTran(context.Background())
status = int8(0)
couponToken = ""
mid = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.TxUpdateViewInfo(tx, status, couponToken, mid)
if err == nil {
if err = tx.Commit(); err != nil {
tx.Rollback()
}
} else {
tx.Rollback()
}
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxCouponViewLog(t *testing.T) {
convey.Convey("TxCouponViewLog", t, func(convCtx convey.C) {
var (
tx, _ = d.BeginTran(context.Background())
arg = &model.CouponChangeLog{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.TxCouponViewLog(tx, arg)
if err == nil {
if err = tx.Commit(); err != nil {
tx.Rollback()
}
} else {
tx.Rollback()
}
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoCouponViewInfo(t *testing.T) {
convey.Convey("CouponViewInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
couponToken = ""
mid = int64(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
r, err := d.CouponViewInfo(c, couponToken, mid)
convCtx.Convey("Then err should be nil.r should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(r, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSearchViewCouponCount(t *testing.T) {
convey.Convey("SearchViewCouponCount", t, func(convCtx convey.C) {
var (
c = context.Background()
arg = &model.ArgSearchCouponView{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
count, err := d.SearchViewCouponCount(c, arg)
convCtx.Convey("Then err should be nil.count should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(count, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSearchViewCouponInfo(t *testing.T) {
convey.Convey("SearchViewCouponInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
arg = &model.ArgSearchCouponView{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.SearchViewCouponInfo(c, arg)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoBatchAddAllowanceCoupon(t *testing.T) {
convey.Convey("BatchAddAllowanceCoupon", t, func(convCtx convey.C) {
var (
c = context.Background()
b bytes.Buffer
tx, _ = d.BeginTran(context.Background())
cps = []*model.CouponAllowanceInfo{}
)
b.WriteString(fmt.Sprintf("%05d", 1))
b.WriteString(fmt.Sprintf("%02d", rand.Int63n(99)))
b.WriteString(fmt.Sprintf("%03d", time.Now().UnixNano()/1e6%1000))
b.WriteString(time.Now().Format("20060102150405"))
cp := &model.CouponAllowanceInfo{CouponToken: b.String()}
cps = append(cps, cp)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
a, err := d.BatchAddAllowanceCoupon(c, tx, cps)
if err == nil {
if err = tx.Commit(); err != nil {
tx.Rollback()
}
} else {
tx.Rollback()
}
convCtx.Convey("Then err should be nil.a should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(a, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateBatchInfo(t *testing.T) {
convey.Convey("UpdateBatchInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
tx, _ = d.BeginTran(context.Background())
token = ""
count = int(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
a, err := d.UpdateBatchInfo(c, tx, token, count)
if err == nil {
if err = tx.Commit(); err != nil {
tx.Rollback()
}
} else {
tx.Rollback()
}
convCtx.Convey("Then err should be nil.a should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(a, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,32 @@
package dao
import (
"context"
"go-common/app/admin/main/coupon/model"
seasongrpc "go-common/app/service/openplatform/pgc-season/api/grpc/season/v1"
"github.com/pkg/errors"
)
//GetPGCInfo get pgc info.
func (d *Dao) GetPGCInfo(c context.Context, oid int32) (r *model.PGCInfoResq, err error) {
var (
params *seasongrpc.SeasonInfoReq
oids = make([]int32, 0)
reply *seasongrpc.CardsInfoReply
)
oids = append(oids, oid)
params = &seasongrpc.SeasonInfoReq{
SeasonIds: oids,
}
if reply, err = d.rpcClient.Cards(c, params); err != nil {
err = errors.WithStack(err)
return
}
if proto, success := reply.Cards[oid]; success {
r = new(model.PGCInfoResq)
r.Title = proto.Title
}
return
}

View File

@@ -0,0 +1,23 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoGetPGCInfo(t *testing.T) {
convey.Convey("GetPGCInfo", t, func(convCtx convey.C) {
var (
c = context.Background()
oid = int32(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
_, err := d.GetPGCInfo(c, oid)
convCtx.Convey("Then err should be nil.r should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"allowance.go",
"code.go",
"coupon.go",
"http.go",
"view.go",
],
importpath = "go-common/app/admin/main/coupon/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/coupon/conf:go_default_library",
"//app/admin/main/coupon/model:go_default_library",
"//app/admin/main/coupon/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/binding:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/xstr: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,268 @@
package http
import (
"bytes"
"io"
"mime/multipart"
"go-common/app/admin/main/coupon/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
"go-common/library/xstr"
)
// batchadd add.
func allowanceBatchadd(c *bm.Context) {
var err error
arg := new(model.ArgAllowanceBatchInfo)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
if _, ok := model.ProdLimMonthMap[arg.ProdLimMonth]; !ok {
c.JSON(nil, ecode.RequestErr)
return
}
b := new(model.CouponBatchInfo)
b.AppID = arg.AppID
b.Name = arg.Name
if arg.MaxCount == 0 {
b.MaxCount = -1
} else {
b.MaxCount = arg.MaxCount
}
if arg.LimitCount == 0 {
b.LimitCount = -1
} else {
b.LimitCount = arg.LimitCount
}
b.StartTime = arg.StartTime
b.ExpireTime = arg.ExpireTime
b.Operator = operator.(string)
b.Amount = arg.Amount
b.FullAmount = arg.FullAmount
b.ExpireDay = arg.ExpireDay
b.PlatformLimit = xstr.JoinInts(arg.PlatformLimit)
b.ProdLimMonth = arg.ProdLimMonth
b.ProdLimRenewal = arg.ProdLimRenewal
b.CouponType = model.CouponAllowance
_, err = svc.AddAllowanceBatchInfo(c, b)
c.JSON(nil, err)
}
// batchadd allowance modify.
func allowanceBatchModify(c *bm.Context) {
var err error
arg := new(model.ArgAllowanceBatchInfoModify)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
b := new(model.CouponBatchInfo)
b.AppID = arg.AppID
b.Name = arg.Name
if arg.MaxCount == 0 {
b.MaxCount = -1
} else {
b.MaxCount = arg.MaxCount
}
if arg.LimitCount == 0 {
b.LimitCount = -1
} else {
b.LimitCount = arg.LimitCount
}
b.Operator = operator.(string)
b.PlatformLimit = xstr.JoinInts(arg.PlatformLimit)
b.ProdLimMonth = arg.ProdLimMonth
b.ProdLimRenewal = arg.ProdLimRenewal
b.ID = arg.ID
c.JSON(nil, svc.UpdateAllowanceBatchInfo(c, b))
}
// allowanceBlock .
func allowanceBatchBlock(c *bm.Context) {
var err error
arg := new(model.ArgAllowance)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.UpdateBatchStatus(c, model.BatchStateBlock, operator.(string), arg.ID))
}
// allowanceUnBlock .
func allowanceBatchUnBlock(c *bm.Context) {
var err error
arg := new(model.ArgAllowance)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.UpdateBatchStatus(c, model.BatchStateNormal, operator.(string), arg.ID))
}
func allowanceSalary(c *bm.Context) {
var (
f multipart.File
h *multipart.FileHeader
err error
)
arg := new(model.ArgAllowanceSalary)
if err = c.BindWith(arg, binding.FormMultipart); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
if len(arg.Mids) <= 0 {
f, h, err = c.Request.FormFile("file")
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
}
c.JSON(svc.AllowanceSalary(c, f, h, arg.Mids, arg.BatchToken, arg.MsgType))
}
func batchInfo(c *bm.Context) {
var (
err error
)
arg := new(model.ArgAllowanceInfo)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
_, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(svc.BatchInfo(c, arg.BatchToken))
}
// func allowancePage(c *bm.Context) {
// var err error
// arg := new(model.ArgAllowanceSearch)
// if err = c.Bind(arg); err != nil {
// log.Error("c.Bind err(%+v)", err)
// return
// }
// _, ok := c.Get("username")
// if !ok {
// c.JSON(nil, ecode.AccessDenied)
// return
// }
// c.JSON(svc.AllowancePage(c, arg))
// }
func allowanceList(c *bm.Context) {
var err error
arg := new(model.ArgAllowanceSearch)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
_, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(svc.AllowanceList(c, arg))
}
func allowanceBlock(c *bm.Context) {
var (
err error
)
arg := new(model.ArgAllowanceState)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
_, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.UpdateAllowanceState(c, arg.Mid, model.Block, arg.CouponToken))
}
func allowanceUnBlock(c *bm.Context) {
var err error
arg := new(model.ArgAllowanceState)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
_, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.UpdateAllowanceState(c, arg.Mid, model.NotUsed, arg.CouponToken))
}
func batchSalaryCoupon(c *bm.Context) {
var err error
req := new(model.ArgBatchSalaryCoupon)
if err = c.Bind(req); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
_, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.ActivitySalaryCoupon(c, req))
}
func uploadFile(c *bm.Context) {
var (
f multipart.File
err error
)
_, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
arg := new(model.ArgUploadFile)
if err = c.BindWith(arg, binding.FormMultipart); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
f, _, err = c.Request.FormFile("file")
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
defer f.Close()
buf := new(bytes.Buffer)
if _, err = io.Copy(buf, f); err != nil {
c.JSON(nil, err)
return
}
c.JSON(nil, svc.OutFile(c, buf.Bytes(), arg.FileURL))
}

View File

@@ -0,0 +1,174 @@
package http
import (
"fmt"
"go-common/app/admin/main/coupon/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
func exportCode(c *bm.Context) {
var (
err error
codes []string
)
arg := new(model.ArgCouponCode)
if err = c.Bind(arg); err != nil {
return
}
if codes, err = svc.ExportCode(c, arg); err != nil {
c.JSON(nil, err)
return
}
writer := c.Writer
header := writer.Header()
header.Add("Content-disposition", "attachment; filename="+fmt.Sprintf("%v", arg.BatchToken)+".txt")
header.Add("Content-Type", "application/x-download;charset=utf-8")
for _, v := range codes {
writer.Write([]byte(fmt.Sprintf("%v\r\n", v)))
}
}
func codePage(c *bm.Context) {
var err error
arg := new(model.ArgCouponCode)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(svc.CodePage(c, arg))
}
func codeBlock(c *bm.Context) {
var err error
arg := new(model.ArgCouponCode)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(nil, svc.CodeBlock(c, arg))
}
func codeUnBlock(c *bm.Context) {
var err error
arg := new(model.ArgCouponCode)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(nil, svc.CodeUnBlock(c, arg))
}
func codeAddBatch(c *bm.Context) {
var (
err error
token string
)
arg := new(model.ArgAllowanceBatchInfo)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
if _, ok := model.ProdLimMonthMap[arg.ProdLimMonth]; !ok {
c.JSON(nil, ecode.RequestErr)
return
}
b := new(model.CouponBatchInfo)
b.AppID = arg.AppID
b.Name = arg.Name
if arg.MaxCount == 0 || arg.MaxCount > model.BatchCodeMaxCount {
c.JSON(nil, ecode.CouponCodeMaxLimitErr)
return
}
b.MaxCount = arg.MaxCount
if arg.LimitCount == 0 {
b.LimitCount = -1
} else {
b.LimitCount = arg.LimitCount
}
b.StartTime = arg.StartTime
b.ExpireTime = arg.ExpireTime
b.Operator = operator.(string)
b.Amount = arg.Amount
b.FullAmount = arg.FullAmount
b.ExpireDay = arg.ExpireDay
b.PlatformLimit = xstr.JoinInts(arg.PlatformLimit)
b.ProdLimMonth = arg.ProdLimMonth
b.ProdLimRenewal = arg.ProdLimRenewal
b.CouponType = model.CouponAllowanceCode
if token, err = svc.AddAllowanceBatchInfo(c, b); err != nil {
c.JSON(nil, err)
return
}
c.JSON(nil, svc.InitCodes(c, token))
}
func codeBatchModify(c *bm.Context) {
var err error
arg := new(model.ArgAllowanceBatchInfoModify)
if err = c.Bind(arg); err != nil {
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
b := new(model.CouponBatchInfo)
b.AppID = arg.AppID
b.Name = arg.Name
if arg.LimitCount == 0 {
b.LimitCount = -1
} else {
b.LimitCount = arg.LimitCount
}
b.Operator = operator.(string)
b.PlatformLimit = xstr.JoinInts(arg.PlatformLimit)
b.ProdLimMonth = arg.ProdLimMonth
b.ProdLimRenewal = arg.ProdLimRenewal
b.ID = arg.ID
c.JSON(nil, svc.UpdateCodeBatchInfo(c, b))
}
func codeBatchList(c *bm.Context) {
var err error
arg := new(model.ArgBatchList)
if err = c.Bind(arg); err != nil {
return
}
arg.Type = model.CouponAllowanceCode
c.JSON(svc.BatchList(c, arg))
}
func codeBatchBlock(c *bm.Context) {
var err error
arg := new(model.ArgAllowance)
if err = c.Bind(arg); err != nil {
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.UpdateBatchStatus(c, model.BatchStateBlock, operator.(string), arg.ID))
}
func codeBatchUnBlock(c *bm.Context) {
var err error
arg := new(model.ArgAllowance)
if err = c.Bind(arg); err != nil {
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.UpdateBatchStatus(c, model.BatchStateNormal, operator.(string), arg.ID))
}

View File

@@ -0,0 +1,115 @@
package http
import (
"go-common/app/admin/main/coupon/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
// batchadd add.
func batchadd(c *bm.Context) {
var (
err error
)
arg := new(model.ArgBatchInfo)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
b := new(model.CouponBatchInfo)
b.AppID = arg.AppID
b.Name = arg.Name
if arg.MaxCount == 0 {
b.MaxCount = -1
} else {
b.MaxCount = arg.MaxCount
}
if arg.LimitCount == 0 {
b.LimitCount = -1
} else {
b.LimitCount = arg.LimitCount
}
b.StartTime = arg.StartTime
b.ExpireTime = arg.ExpireTime
b.Operator = operator.(string)
if err = svc.AddBatchInfo(c, b); err != nil {
log.Error("svc.AddBatchInfo(%v) err(%+v)", arg, err)
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
}
func batchlist(c *bm.Context) {
var (
err error
res []*model.CouponBatchResp
)
arg := new(model.ArgBatchList)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
if res, err = svc.BatchList(c, arg); err != nil {
log.Error("svc.BatchList(%v) err(%+v)", arg, err)
c.JSON(nil, err)
return
}
c.JSON(res, nil)
}
func allAppInfo(c *bm.Context) {
c.JSON(svc.AllAppInfo(c), nil)
}
func salaryCoupon(c *bm.Context) {
var err error
arg := new(model.ArgSalaryCoupon)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
if err = svc.SalaryCoupon(c, arg.Mid, arg.CouponType, arg.Count, arg.BranchToken); err != nil {
log.Error("svc.SalaryCoupon(%v) err(%+v)", arg, err)
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
}
func batchBlock(c *bm.Context) {
var err error
arg := new(model.ArgAllowance)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.UpdateBatchStatus(c, model.BatchStateBlock, operator.(string), arg.ID))
}
// allowanceUnBlock .
func batchUnBlock(c *bm.Context) {
var err error
arg := new(model.ArgAllowance)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.UpdateBatchStatus(c, model.BatchStateNormal, operator.(string), arg.ID))
}

View File

@@ -0,0 +1,93 @@
package http
import (
"net/http"
"go-common/app/admin/main/coupon/conf"
"go-common/app/admin/main/coupon/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
)
var (
svc *service.Service
authSvc *permit.Permit
)
// Init init
func Init(c *conf.Config) {
initService(c)
// init router
engine := bm.DefaultServer(c.BM)
initRouter(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start error(%v)", err)
panic(err)
}
}
// initService init services.
func initService(c *conf.Config) {
authSvc = permit.New(c.Auth)
svc = service.New(c)
}
// initRouter init outer router api path.
func initRouter(e *bm.Engine) {
//init api
e.Ping(ping)
group := e.Group("/x/admin/coupon", authSvc.Permit("CONPON"))
group.GET("/batch/list", batchlist)
group.POST("/batch/add", batchadd)
group.GET("/appinfo/list", allAppInfo)
group.POST("/salary", salaryCoupon)
allowance := group.Group("/allowance", authSvc.Permit("CONPON_ALLOWANCE"))
allowance.GET("/batch/list", batchlist)
allowance.GET("/batch/info", batchInfo)
allowance.POST("/batch/block", allowanceBatchBlock)
allowance.POST("/batch/unblock", allowanceBatchUnBlock)
allowance.POST("/batch/add", allowanceBatchadd)
allowance.POST("/batch/modify", allowanceBatchModify)
allowance.GET("/list", allowanceList)
allowance.POST("/block", allowanceBlock)
allowance.POST("/unblock", allowanceUnBlock)
allowance.POST("/salary", allowanceSalary)
allowance.POST("/activity/salary", batchSalaryCoupon)
allowance.POST("/uploadfile", uploadFile)
view := group.Group("/view", authSvc.Permit("COUPON_VIEW"))
{
view.GET("/batch/list", batchlist)
view.GET("/batch/info", batchInfo)
view.POST("/batch/block", batchBlock)
view.POST("/batch/unblock", batchUnBlock)
view.POST("/batch/add", viewBatchAdd)
view.POST("/batch/save", viewBatchSave)
view.GET("/list", viewList)
view.POST("/block", viewBlock)
view.POST("/unblock", viewUnblock)
view.POST("/salary", salaryView)
}
// code
group.GET("/batch_code/list", codeBatchList)
group.POST("/batch_code/block", codeBatchBlock)
group.POST("/batch_code/unblock", codeBatchUnBlock)
group.POST("/batch_code/add", codeAddBatch)
group.POST("/batch_code/modify", codeBatchModify)
group.GET("/code/list", codePage)
group.POST("/code/block", codeBlock)
group.POST("/code/unblock", codeUnBlock)
group.GET("/code/export", exportCode)
}
// ping check server ok.
func ping(c *bm.Context) {
if err := svc.Ping(c); err != nil {
log.Error("coupon http admin ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}

View File

@@ -0,0 +1,115 @@
package http
import (
"mime/multipart"
"go-common/app/admin/main/coupon/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func viewBatchAdd(c *bm.Context) {
var err error
arg := new(model.ArgCouponViewBatch)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
arg.Operator = operator.(string)
c.JSON(nil, svc.CouponViewBatchAdd(c, arg))
}
func viewBatchSave(c *bm.Context) {
var err error
arg := new(model.ArgCouponViewBatch)
if err = c.Bind(arg); err != nil {
log.Error("c.Bind err(%+v)", err)
return
}
operator, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
arg.Operator = operator.(string)
c.JSON(nil, svc.CouponViewbatchSave(c, arg))
}
func viewBlock(c *bm.Context) {
var err error
arg := new(struct {
Mid int64 `form:"mid" validate:"required"`
CouponToken string `form:"coupon_token" validate:"required"`
})
if err = c.Bind(arg); err != nil {
log.Error("c.bind err(%+v)", err)
return
}
_, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.CouponViewBlock(c, arg.Mid, arg.CouponToken))
}
func viewUnblock(c *bm.Context) {
var err error
arg := new(struct {
Mid int64 `form:"mid" validate:"required"`
CouponToken string `form:"coupon_token" validate:"required"`
})
if err = c.Bind(arg); err != nil {
log.Error("c.bind err(%+v)", err)
return
}
_, ok := c.Get("username")
if !ok {
c.JSON(nil, ecode.AccessDenied)
return
}
c.JSON(nil, svc.CouponViewUnblock(c, arg.Mid, arg.CouponToken))
}
func viewList(c *bm.Context) {
var err error
arg := new(model.ArgSearchCouponView)
if err = c.Bind(arg); err != nil {
log.Error("c.bind err(%+v)", err)
return
}
res, count, err := svc.CouponViewList(c, arg)
info := new(model.PageInfo)
info.CurrentPage = arg.PN
info.Count = int(count)
info.Item = res
c.JSON(info, err)
}
func salaryView(c *bm.Context) {
var (
f multipart.File
h *multipart.FileHeader
err error
)
arg := new(model.ArgAllowanceSalary)
if err = c.BindWith(arg, binding.FormMultipart); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
if len(arg.Mids) <= 0 {
f, h, err = c.Request.FormFile("file")
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
}
c.JSON(svc.CouponViewSalary(c, f, h, arg.Mids, arg.BatchToken))
}

View File

@@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"model.go",
"search.go",
],
importpath = "go-common/app/admin/main/coupon/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//library/time: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,415 @@
package model
import (
"go-common/library/time"
)
//allowance origin
const (
AllowanceNone = iota
AllowanceSystemAdmin
AllowanceBusinessReceive
)
// blance change type
const (
VipSalary int64 = iota + 1
SystemAdminSalary
Consume
ConsumeFaildBack
)
// coupon type
const (
CouponVideo = iota + 1
CouponCartoon
CouponAllowance
CouponAllowanceCode
)
// coupon state.
const (
NotUsed = iota
InUse
Used
Expire
Block
)
// allowance explain
const (
NoLimitExplain = "不限定"
ScopeFmt = "仅限%s端使用"
)
// batch state
const (
BatchStateNormal int8 = iota
BatchStateBlock
)
// batch origin
const (
AdminSalaryOrigin int64 = iota + 1
)
// allowance change type
const (
AllowanceSalary int8 = iota + 1
AllowanceConsume
AllowanceCancel
AllowanceConsumeSuccess
AllowanceConsumeFaild
AllowanceBlock
AllowanceUnBlock
)
// coupon_batch_info表 product_limit_renewal字段.
const (
ProdLimRenewalAll int8 = iota
ProdLimRenewalAuto
ProdLimRenewalNotAuto
)
// coupon_batch_info表 product_limit_renewal字段.
const (
None int8 = 0
ProdLimMonth1 = 1
ProdLimMonth3 = 3
ProdLimMonth12 = 12
)
// ProdLimit .
var (
ProdLimMonthMap = map[int8]string{None: "", ProdLimMonth1: "1月", ProdLimMonth3: "3月", ProdLimMonth12: "12月"}
ProdLimRenewalMap = map[int8]string{ProdLimRenewalAll: "", ProdLimRenewalAuto: "自动续期", ProdLimRenewalNotAuto: "非自动续期"}
)
// PageInfo common page info.
type PageInfo struct {
Count int `json:"count"`
CurrentPage int `json:"currentPage,omitempty"`
Item interface{} `json:"item"`
}
// CouponBatchInfo info.
type CouponBatchInfo struct {
ID int64 `json:"id"`
AppID int64 `json:"app_id"`
Name string `json:"name"`
BatchToken string `json:"batch_token"`
MaxCount int64 `json:"max_count"`
CurrentCount int64 `json:"current_count"`
StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"`
ExpireDay int64 `json:"expire_day"`
Ver int64 `json:"ver"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
Operator string `json:"operator"`
LimitCount int64 `json:"limit_count"`
FullAmount float64 `json:"full_amount"`
Amount float64 `json:"amount"`
State int8 `json:"state"`
CouponType int8 `json:"coupon_type"`
PlatformLimit string `json:"platform_limit"`
ProdLimMonth int8 `json:"product_limit_month"`
ProdLimRenewal int8 `json:"product_limit_Renewal"`
}
// ArgBatchInfo arg.
type ArgBatchInfo struct {
AppID int64 `form:"app_id" validate:"required,min=1,gte=1"`
Name string `form:"name" validate:"required"`
MaxCount int64 `form:"max_count" validate:"required,min=1,gte=1"`
LimitCount int64 `form:"limit_count"`
StartTime int64 `form:"start_time" validate:"required,min=1,gte=1"`
ExpireTime int64 `form:"end_time" validate:"required,min=1,gte=1"`
}
// ArgAllowanceBatchInfo allowance arg.
type ArgAllowanceBatchInfo struct {
AppID int64 `form:"app_id" validate:"required,min=1,gte=1"`
Name string `form:"name" validate:"required"`
MaxCount int64 `form:"max_count"`
LimitCount int64 `form:"limit_count"`
StartTime int64 `form:"start_time"`
ExpireTime int64 `form:"end_time"`
ExpireDay int64 `form:"expire_day" default:"-1"`
Amount float64 `form:"amount" validate:"required,min=1,gte=1"`
FullAmount float64 `form:"full_amount" validate:"required,min=1,gte=1"`
PlatformLimit []int64 `form:"platform_limit,split"`
ProdLimMonth int8 `form:"product_limit_month"`
ProdLimRenewal int8 `form:"product_limit_Renewal" validate:"gte=0,lte=2"`
}
// ArgAllowanceBatchInfoModify allowance modify arg.
type ArgAllowanceBatchInfoModify struct {
ID int64 `form:"id" validate:"required,min=1,gte=1"`
AppID int64 `form:"app_id" validate:"required,min=1,gte=1"`
Name string `form:"name" validate:"required"`
MaxCount int64 `form:"max_count" `
LimitCount int64 `form:"limit_count"`
PlatformLimit []int64 `form:"platform_limit,split"`
ProdLimMonth int8 `form:"product_limit_month" validate:"gte=0"`
ProdLimRenewal int8 `form:"product_limit_Renewal" validate:"gte=0,lte=2"`
}
// ArgAllowance arg.
type ArgAllowance struct {
ID int64 `form:"id" validate:"required,min=1,gte=1"`
}
// ArgAllowanceInfo arg.
type ArgAllowanceInfo struct {
BatchToken string `form:"batch_token" validate:"required"`
}
// ArgAllowanceSalary allowance salary arg.
type ArgAllowanceSalary struct {
Mids []int64 `form:"mids,split"`
BatchToken string `form:"batch_token" validate:"required"`
MsgType string `form:"msg_type" default:"vip"`
}
// ArgAllowanceState arg.
type ArgAllowanceState struct {
Mid int64 `form:"mid" validate:"required,min=1,gte=1"`
CouponToken string `form:"coupon_token" validate:"required"`
}
// ArgBatchList arg.
type ArgBatchList struct {
AppID int64 `form:"app_id"`
Type int8 `form:"type" default:"3"`
}
// ArgSalaryCoupon salary coupon.
type ArgSalaryCoupon struct {
Mid int64 `form:"mid" validate:"required,min=1,gte=1"`
CouponType int64 `form:"coupon_type" validate:"required,min=1,gte=1"`
Count int `form:"count" validate:"required,min=1,gte=1"`
BranchToken string `form:"branch_token" validate:"required"`
}
// ArgUploadFile upload file arg.
type ArgUploadFile struct {
FileURL string `form:"url" validate:"required"`
}
// CouponBatchResp resp.
type CouponBatchResp struct {
ID int64 `json:"id"`
AppID int64 `json:"app_id"`
AppName string `json:"app_name"`
Name string `json:"name"`
BatchToken string `json:"batch_token"`
MaxCount int64 `json:"max_count"`
CurrentCount int64 `json:"current_count"`
StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"`
ExpireDay int64 `json:"expire_day"`
Operator string `json:"operator"`
LimitCount int64 `json:"limit_count"`
ProductLimitExplain string `json:"product_limit_explain"`
PlatfromLimit []int64 `json:"platform_limit"`
UseLimitExplain string `json:"use_limit_explain"`
State int8 `json:"state"`
Amount float64 `json:"amount"`
FullAmount float64 `json:"full_amount"`
ProdLimMonth int8 `json:"product_limit_month"`
ProdLimRenewal int8 `json:"product_limit_Renewal"`
}
// AppInfo app info.
type AppInfo struct {
ID int64 `json:"id"`
Name string `json:"name"`
Appkey string `json:"appkey"`
NotifyURL string `json:"notify_url"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}
// CouponResp def.
type CouponResp struct {
Token string `json:"token"`
Mid int64 `json:"mid"`
GrantTime int64 `json:"grant_time"`
UseTime int64 `json:"use_time"`
State int8 `json:"state"`
Remark int8 `json:"remark"`
}
// CouponAllowanceInfo coupon allowance info.
type CouponAllowanceInfo struct {
ID int64 `json:"id"`
CouponToken string `json:"coupon_token"`
Mid int64 `json:"mid"`
State int32 `json:"state"`
StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"`
Origin int64 `json:"origin"`
OrderNO string `json:"order_no"`
Ver int64 `json:"ver"`
Remark string `json:"remark"`
CTime time.Time `json:"ctime"`
MTime time.Time `json:"mtime"`
BatchToken string `json:"batch_token"`
Amount float64 `json:"amount"`
FullAmount float64 `json:"full_amount"`
AppID int64 `json:"app_id"`
}
// CouponAllowanceChangeLog coupon allowance change log.
type CouponAllowanceChangeLog struct {
ID int64 `json:"-"`
CouponToken string `json:"coupon_token"`
OrderNO string `json:"order_no"`
Mid int64 `json:"mid"`
State int8 `json:"state"`
ChangeType int8 `json:"change_type"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}
// ProdLimExplainFmt .
func (c *CouponBatchResp) ProdLimExplainFmt(prodLimMonth, prodLimRenewal int8) {
if prodLimMonth == None && prodLimRenewal == None {
c.ProductLimitExplain = NoLimitExplain
}
pstr := ""
if limm, ok := ProdLimMonthMap[prodLimMonth]; ok {
pstr += limm
}
if limr, ok := ProdLimRenewalMap[prodLimRenewal]; ok {
pstr += "、" + limr
}
c.ProductLimitExplain = pstr
}
//Sizer .
type Sizer interface {
Size() int64
}
//ArgCouponViewBatch .
type ArgCouponViewBatch struct {
ID int64 `form:"id"`
Name string `form:"name" validate:"required"`
AppID int64 `form:"app_id" validate:"required,min=1"`
MaxCount int64 `form:"max_count" default:"-1"`
CurrentCount int64 `form:"current_count"`
LimitCount int64 `form:"limit_count" default:"-1"`
StartTime int64 `form:"start_time" validate:"required,min=1"`
ExpireTime int64 `form:"end_time" validate:"required,min=1"`
Operator string `form:"operator"`
Ver int64
BatchToken string
CouponType int8
}
//ArgSearchCouponView .
type ArgSearchCouponView struct {
PN int `form:"pn" default:"1"`
PS int `form:"ps" default:"20"`
Mid int64 `form:"mid" validate:"required"`
CouponToken string `form:"coupon_token"`
AppID int64 `form:"app_id"`
BatchToken string `form:"batch_token"`
BatchTokens []string
}
//CouponInfo .
type CouponInfo struct {
CouponToken string `json:"coupon_token"`
Mid int64 `json:"mid"`
State int8 `json:"state"`
StartTime int64 `json:"start_time"`
ExpireTime int64 `json:"expire_time"`
Origin int8 `json:"origin"`
CouponType int8 `json:"coupon_type"`
OrderNo string `json:"order_no"`
OID int32 `json:"oid"`
Remark string `json:"remark"`
UseVer int64 `json:"use_ver"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
BatchToken string `json:"batch_token"`
Title string `json:"title"`
BatchName string `json:"batch_name"`
}
//PGCInfoResq .
type PGCInfoResq struct {
Title string `json:"title"`
}
//CouponChangeLog .
type CouponChangeLog struct {
CouponToken string `json:"coupon_token"`
Mid int64 `json:"mid"`
State int8 `json:"state"`
}
// ArgBatchSalaryCoupon batch salary coupon.
type ArgBatchSalaryCoupon struct {
FileURL string `form:"file_url" validate:"required"`
Count int64 `form:"count" validate:"required,min=1,gte=1"`
BranchToken string `form:"branch_token" validate:"required"`
SliceSize int `form:"slice_size" default:"100" validate:"min=100,max=10000"`
}
// ArgCouponCode coupon code.
type ArgCouponCode struct {
ID int64 `form:"id"`
BatchToken string `form:"batch_token"`
State int32 `form:"state"`
Code string `form:"code"`
Mid int64 `form:"mid"`
CouponType int32 `form:"coupon_type"`
CouponToken string `form:"coupon_token"`
Pn int `form:"pn"`
Ps int `form:"ps"`
}
// CouponCode coupon code.
type CouponCode struct {
ID int64 `json:"id"`
BatchToken string `json:"batch_token"`
State int32 `json:"state"`
Code string `json:"code"`
Mid int64 `json:"mid"`
CouponType int32 `json:"coupon_type"`
CouponToken string `json:"coupon_token"`
Ver int64 `json:"ver"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}
// CodePage code page.
type CodePage struct {
Count int64 `json:"count"`
CodeList []*CouponCode `json:"code_list"`
}
// coupon code state.
const (
CodeStateNotUse = iota + 1
CodeStateUsed
CodeStateBlock
CodeStateExpire
)
// batch code max count.
const (
BatchCodeMaxCount = 50000
BatchAddCodeSlice = 100
)
// code batch state.
const (
CodeBatchUsable = iota
CodeBatchBlock
CodeBatchExpire
)

View File

@@ -0,0 +1,36 @@
package model
// SearchData search result detail.
type SearchData struct {
Code int `json:"code"`
Data *struct {
Order string `json:"order"`
Sort string `json:"sort"`
Page *SearchPage `json:"page"`
Result []*CouponAllowanceInfo `json:"result"`
}
}
// SearchPage struct.
type SearchPage struct {
PN int `json:"num"`
PS int `json:"size"`
Total int `json:"total"`
}
// ArgAllowanceSearch allowance search struct
type ArgAllowanceSearch struct {
PN int `form:"pn" default:"1"`
PS int `form:"ps" default:"20"`
AppID int64 `form:"app_id"`
Mid int64 `form:"mid" validate:"required,min=1,gte=1"`
OrderNO string `form:"order_no"`
CouponToken string `form:"coupon_token"`
BatchToken string `form:"batch_token"`
}
// PageCouponInfo common page info.
type PageCouponInfo struct {
Count int `json:"count"`
Item interface{} `json:"item"`
}

View File

@@ -0,0 +1,69 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"activity.go",
"allowance.go",
"code.go",
"coupon.go",
"service.go",
"view.go",
],
importpath = "go-common/app/admin/main/coupon/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/coupon/conf:go_default_library",
"//app/admin/main/coupon/dao:go_default_library",
"//app/admin/main/coupon/model:go_default_library",
"//app/service/main/coupon/model:go_default_library",
"//app/service/main/coupon/rpc/client:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//library/time:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors: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"],
)
go_test(
name = "go_default_test",
srcs = [
"activity_test.go",
"allowance_test.go",
"code_test.go",
"coupon_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/coupon/conf:go_default_library",
"//app/admin/main/coupon/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,273 @@
package service
import (
"bufio"
"bytes"
"context"
"encoding/csv"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"time"
"go-common/app/admin/main/coupon/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
xtime "go-common/library/time"
"go-common/library/xstr"
"github.com/pkg/errors"
)
// ActivitySalaryCoupon activity salary coupon.
func (s *Service) ActivitySalaryCoupon(c context.Context, req *model.ArgBatchSalaryCoupon) (err error) {
var (
ok bool
r *model.CouponBatchInfo
)
if r, err = s.dao.BatchInfo(c, req.BranchToken); err != nil {
return
}
if r == nil {
return ecode.CouPonBatchNotExistErr
}
if r.State != model.BatchStateNormal {
return ecode.CouPonHadBlockErr
}
if r.ExpireDay != -1 {
return ecode.CouponTypeNotSupportErr
}
if ok = s.dao.AddGrantUniqueLock(c, req.BranchToken, _lockseconds); !ok {
return ecode.CouponSalaryHadRunErr
}
// mids index
s.runSalary(metadata.String(c, metadata.RemoteIP), r, req)
return
}
// runSalary run batch salary.
func (s *Service) runSalary(ip string, b *model.CouponBatchInfo, req *model.ArgBatchSalaryCoupon) (err error) {
go func() {
var (
err error
fails []int64
tmp []int64
mids = []int64{}
total int64
midmap map[int64][]int64
c = context.Background()
)
defer func() {
if x := recover(); x != nil {
x = errors.WithStack(x.(error))
log.Error("runtime error caught: %+v", x)
}
//Write fail mids
if len(fails) > 0 {
s.OutFile(c, []byte(xstr.JoinInts(fails)), req.BranchToken+"_failmids.csv")
}
}()
log.Info("batch salary analysis slary file[req(%v)]", req)
// analysis slary file
if midmap, total, err = s.AnalysisFile(c, req.FileURL); err != nil {
return
}
if total != req.Count {
log.Error("[batch salary count err req(%v),filetotal(%d)]", req, total)
s.dao.DelGrantUniqueLock(c, b.BatchToken)
return
}
log.Info("batch salary start[count(%d),token(%s)]", total, req.BranchToken)
var crrent int
for _, allmids := range midmap {
for i, v := range allmids {
mids = append(mids, v)
if len(mids)%req.SliceSize != 0 && i != len(allmids)-1 {
continue
}
crrent = crrent + len(mids)
log.Info("batch salary ing[mid(%d),i(%d),token(%s),crrent(%d)]", v, i, req.BranchToken, crrent)
if tmp, err = s.batchSalary(c, mids, ip, b); err != nil && len(tmp) > 0 {
fails = append(fails, tmp...)
log.Error("batch salary err[mids(%v),i(%d),token(%s)] err(%v)", mids, i, req.BranchToken, err)
}
mids = []int64{}
}
}
log.Info("batch salary end[count(%d),token(%s),faillen(%d)]", total, req.BranchToken, len(fails))
}()
return
}
// batchSalary batch salary.
func (s *Service) batchSalary(c context.Context, mids []int64, ip string, b *model.CouponBatchInfo) (falls []int64, err error) {
var (
tx *sql.Tx
aff int64
)
if tx, err = s.dao.BeginTran(c); err != nil {
return
}
defer func() {
if err != nil {
falls = mids
if err1 := tx.Rollback(); err1 != nil {
log.Error("batch salary rollback: %+v", err1)
}
return
}
if err = tx.Commit(); err != nil {
falls = mids
log.Error("batch salary commit: %+v", err)
tx.Rollback()
return
}
//del cache
s.cache.Do(c, func(c context.Context) {
for _, v := range mids {
if err = s.dao.DelCouponAllowancesKey(context.Background(), v, model.NotUsed); err != nil {
log.Error("batch salary cache del(%d) err: %+v", v, err)
}
}
})
//send msg
if s.c.Prop.SalaryMsgOpen {
s.sendMsg(mids, ip, 1, true, _msgMeng)
}
time.Sleep(time.Duration(s.c.Prop.SalarySleepTime))
}()
cps := make([]*model.CouponAllowanceInfo, len(mids))
for i, v := range mids {
cps[i] = &model.CouponAllowanceInfo{
CouponToken: s.tokeni(i),
Mid: v,
State: model.NotUsed,
StartTime: b.StartTime,
ExpireTime: b.ExpireTime,
Origin: model.AllowanceSystemAdmin,
CTime: xtime.Time(time.Now().Unix()),
BatchToken: b.BatchToken,
Amount: b.Amount,
FullAmount: b.FullAmount,
AppID: b.AppID,
}
}
// mid should be same index.
if aff, err = s.dao.BatchAddAllowanceCoupon(c, tx, cps); err != nil {
return
}
if len(mids) != int(aff) {
err = fmt.Errorf("batch salary midslen(%d) != aff(%d)", len(mids), aff)
return
}
if _, err = s.dao.UpdateBatchInfo(c, tx, b.BatchToken, len(mids)); err != nil {
return
}
return
}
// AnalysisFile analysis slary file.
func (s *Service) AnalysisFile(c context.Context, fileURL string) (midmap map[int64][]int64, total int64, err error) {
cntb, err := ioutil.ReadFile(fileURL)
if err != nil {
return
}
var (
mid int64
records [][]string
)
r := csv.NewReader(strings.NewReader(string(cntb)))
records, err = r.ReadAll()
if err != nil {
err = errors.WithStack(err)
return
}
midmap = make(map[int64][]int64, s.c.Prop.AllowanceTableCount)
for i, v := range records {
if len(v) <= 0 {
continue
}
if mid, err = strconv.ParseInt(v[0], 10, 64); err != nil {
err = errors.Wrapf(err, "read csv line err(%d,%v)", i, v)
break
}
index := mid % s.c.Prop.AllowanceTableCount
if midmap[index] == nil {
midmap[index] = []int64{}
}
midmap[index] = append(midmap[index], mid)
total++
}
return
}
// OutFile out file.
func (s *Service) OutFile(c context.Context, bs []byte, url string) (err error) {
var outFile *os.File
outFile, err = os.Create(url)
if err != nil {
os.Exit(1)
}
defer outFile.Close()
b := bufio.NewWriter(outFile)
_, err = b.Write(bs)
if err != nil {
os.Exit(1)
}
err = b.Flush()
if err != nil {
os.Exit(1)
}
return
}
// get coupon tokeni
func (s *Service) tokeni(i int) string {
var b bytes.Buffer
b.WriteString(fmt.Sprintf("%05d", i))
b.WriteString(fmt.Sprintf("%02d", s.r.Int63n(99)))
b.WriteString(fmt.Sprintf("%03d", time.Now().UnixNano()/1e6%1000))
b.WriteString(time.Now().Format("20060102150405"))
return b.String()
}
func (s *Service) sendMsg(mids []int64, ip string, times int, b bool, msgType string) (err error) {
msg, ok := msgMap[msgType]
if !ok {
log.Warn("sendMsg not support msgType(%s)", msgType)
return
}
for i := 1; i <= times; i++ {
if err = s.dao.SendMessage(context.Background(), xstr.JoinInts(mids), msg["title"], msg["content"], ip); err != nil {
if i != times {
continue
}
if b {
log.Error("batch salary msg send mids(%v) err: %+v", mids, err)
}
}
break
}
return
}
const (
_msgMeng = "meng"
)
var (
msgMap = map[string]map[string]string{
"vip": {
"title": "大会员代金券到账通知",
"content": "大会员代金券已到账快到“我的代金券”看看吧IOS端需要在网页使用。#{\"传送门→\"}{\"https://account.bilibili.com/account/big/voucher\"}",
},
"meng": {
"title": "大会员代金券到账提醒",
"content": "您的专属大会员代金券即将在10月10日0点到账年费半价基础上再享折上折请注意查收代金券有效期1天暂不支持苹果支付 #{\"点击查看详情\"}{\"https://www.bilibili.com/read/cv1291213\"}",
},
}
)

View File

@@ -0,0 +1,59 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/coupon/model"
. "github.com/smartystreets/goconvey/convey"
)
// go test -test.v -test.run TestAnalysisFile
func TestAnalysisFile(t *testing.T) {
Convey("TestAnalysisFile ", t, func() {
res, total, err := s.AnalysisFile(c, "/data/lv4.csv")
t.Logf("res(%v) total(%d)", res, total)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestTokeni
func TestTokeni(t *testing.T) {
Convey("TestTokeni ", t, func() {
token := s.tokeni(100)
t.Logf("token(%s)", token)
So(token, ShouldNotBeBlank)
})
}
// go test -test.v -test.run TestOutFile
func TestOutFile(t *testing.T) {
Convey("TestOutFile ", t, func() {
err := s.OutFile(context.Background(), []byte("haha"), "/data/test.csv")
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestBatchSalary
func TestBatchSalary(t *testing.T) {
Convey("TestbatchSalary ", t, func() {
r, err := s.dao.BatchInfo(c, "allowance_lv41-4")
So(err, ShouldBeNil)
_, err = s.batchSalary(context.Background(), []int64{1, 2, 3}, "127.0.0.1", r)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestActivitySalaryCoupon
func TestActivitySalaryCoupon(t *testing.T) {
Convey("TestActivitySalaryCoupon ", t, func() {
err := s.ActivitySalaryCoupon(c, &model.ArgBatchSalaryCoupon{
FileURL: "/data/1.csv",
Count: 1,
BranchToken: "allowance_lv41-4",
SliceSize: 1000,
})
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,270 @@
package service
import (
"context"
"encoding/csv"
"fmt"
"mime/multipart"
"strconv"
"strings"
"time"
"go-common/app/admin/main/coupon/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
xtime "go-common/library/time"
"github.com/pkg/errors"
)
// AddAllowanceBatchInfo add allowance batch info.
func (s *Service) AddAllowanceBatchInfo(c context.Context, b *model.CouponBatchInfo) (token string, err error) {
if b.ExpireDay < 0 && (b.StartTime <= 0 && b.ExpireTime <= 0) {
err = ecode.CouponExpireErr
return
}
if b.ExpireDay < 0 && b.StartTime >= b.ExpireTime {
err = ecode.CouPonBatchTimeErr
return
}
if b.Amount >= b.FullAmount {
err = ecode.CouponAmountErr
return
}
b.BatchToken = s.token()
b.Ctime = xtime.Time(time.Now().Unix())
if _, err = s.dao.AddAllowanceBatchInfo(c, b); err != nil {
err = errors.WithStack(err)
}
token = b.BatchToken
return
}
//UpdateAllowanceBatchInfo update allowance batch info.
func (s *Service) UpdateAllowanceBatchInfo(c context.Context, b *model.CouponBatchInfo) (err error) {
if _, err = s.dao.UpdateAllowanceBatchInfo(c, b); err != nil {
err = errors.WithStack(err)
}
return
}
//UpdateCodeBatchInfo update code batch info.
func (s *Service) UpdateCodeBatchInfo(c context.Context, b *model.CouponBatchInfo) (err error) {
var data *model.CouponBatchInfo
if data, err = s.BatchInfoByID(c, b.ID); err != nil {
return
}
if data == nil || batchState(data) != model.CodeBatchUsable {
return ecode.RequestErr
}
if _, err = s.dao.UpdateCodeBatchInfo(c, b); err != nil {
err = errors.WithStack(err)
}
return
}
//UpdateBatchStatus update batch status.
func (s *Service) UpdateBatchStatus(c context.Context, status int8, operator string, id int64) (err error) {
var data *model.CouponBatchInfo
if data, err = s.BatchInfoByID(c, id); err != nil {
return
}
if data == nil ||
(status == model.CodeBatchBlock && batchState(data) != model.CodeBatchUsable) ||
(status == model.CodeBatchUsable && batchState(data) != model.CodeBatchBlock) {
return ecode.RequestErr
}
if _, err = s.dao.UpdateBatchStatus(c, status, operator, id); err != nil {
err = errors.WithStack(err)
}
return
}
//BatchInfo allowance batch info.
func (s *Service) BatchInfo(c context.Context, token string) (res *model.CouponBatchInfo, err error) {
if res, err = s.dao.BatchInfo(c, token); err != nil {
err = errors.WithStack(err)
}
return
}
//BatchInfoByID allowance batch info.
func (s *Service) BatchInfoByID(c context.Context, id int64) (*model.CouponBatchInfo, error) {
return s.dao.BatchInfoByID(c, id)
}
// AllowanceSalary allowance salary.
func (s *Service) AllowanceSalary(c context.Context, f multipart.File, h *multipart.FileHeader, mids []int64, token, msgType string) (count int, err error) {
var bi *model.CouponBatchInfo
if len(mids) == 0 {
if mids, err = s.ReadCsv(f, h); err != nil {
err = errors.WithStack(err)
return
}
}
if len(mids) == 0 {
err = ecode.CouponBatchSalaryCountZeroErr
return
}
if len(mids) > _maxSalaryCount {
err = ecode.CouponBatchSalaryLimitErr
return
}
if bi, err = s.dao.BatchInfo(c, token); err != nil {
err = errors.WithStack(err)
return
}
if bi == nil {
err = ecode.CouPonTokenNotFoundErr
return
}
if bi.State != model.BatchStateNormal {
err = ecode.CouPonHadBlockErr
return
}
if bi.ExpireDay < 0 && bi.ExpireTime < time.Now().Unix() {
err = ecode.CouponBatchExpireTimeErr
return
}
if bi.MaxCount != _notLimitSalary && len(mids) > int(bi.MaxCount-bi.CurrentCount) {
err = ecode.CouponBatchSalaryLimitErr
return
}
s.RunSalaryCoupon(c, mids, token, bi.AppID, model.CouponAllowance, model.AdminSalaryOrigin, msgType)
count = len(mids)
return
}
// ReadCsv read csv file
func (s *Service) ReadCsv(f multipart.File, h *multipart.FileHeader) (mids []int64, err error) {
var (
mid int64
records [][]string
)
mids = []int64{}
defer f.Close()
if h != nil && !strings.HasSuffix(h.Filename, ".csv") {
err = ecode.CouponUpdateFileErr
return
}
r := csv.NewReader(f)
records, err = r.ReadAll()
if err != nil {
err = errors.WithStack(err)
return
}
for _, v := range records {
if len(v) <= 0 {
continue
}
if mid, err = strconv.ParseInt(v[0], 10, 64); err != nil {
err = errors.WithStack(err)
break
}
mids = append(mids, mid)
}
return
}
// //AllowancePage allowance page.
// func (s *Service) AllowancePage(c context.Context, arg *model.ArgAllowanceSearch) (res *model.PageCouponInfo, err error) {
// var page *model.SearchData
// res = &model.PageCouponInfo{}
// if page, err = s.dao.AllowancePage(c, arg); err != nil {
// err = errors.WithStack(err)
// return
// }
// if page != nil && page.Data != nil && page.Data.Page != nil {
// res.Count = page.Data.Page.Total
// res.Item = page.Data.Result
// }
// return
// }
//AllowanceList allowance list.
func (s *Service) AllowanceList(c context.Context, arg *model.ArgAllowanceSearch) (res []*model.CouponAllowanceInfo, err error) {
if res, err = s.dao.AllowanceList(c, arg); err != nil {
err = errors.WithStack(err)
}
for _, v := range res {
if v.State == model.NotUsed && v.ExpireTime < time.Now().Unix() {
v.State = model.Expire
}
}
return
}
//UpdateAllowanceState update allowance state.
func (s *Service) UpdateAllowanceState(c context.Context, mid int64, state int8, token string) (err error) {
var (
cp *model.CouponAllowanceInfo
changeType int8
)
if cp, err = s.dao.AllowanceByToken(c, mid, token); err != nil {
err = errors.WithStack(err)
return
}
if cp == nil {
err = ecode.CouPonTokenNotFoundErr
return
}
if state == model.Block {
changeType = model.AllowanceBlock
} else {
changeType = model.AllowanceUnBlock
}
if err = s.UpdateAllowanceCoupon(c, mid, state, token, cp.Ver, changeType, cp); err != nil {
err = errors.WithStack(err)
}
return
}
// UpdateAllowanceCoupon update coupon info.
func (s *Service) UpdateAllowanceCoupon(c context.Context, mid int64, state int8, token string, ver int64, changeType int8, cp *model.CouponAllowanceInfo) (err error) {
var (
tx *sql.Tx
aff int64
)
if tx, err = s.dao.BeginTran(c); err != nil {
log.Error("%+v", err)
return
}
defer func() {
if err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("tx.Rollback %+v", err1)
}
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit %+v", err)
}
}()
if aff, err = s.dao.UpdateAllowanceStatus(c, tx, state, mid, token, ver); err != nil {
err = errors.WithStack(err)
}
if aff != 1 {
err = fmt.Errorf("coupon update faild")
return
}
l := &model.CouponAllowanceChangeLog{
CouponToken: cp.CouponToken,
Mid: cp.Mid,
State: state,
Ctime: xtime.Time(time.Now().Unix()),
OrderNO: cp.OrderNO,
ChangeType: changeType,
}
if aff, err = s.dao.InsertCouponAllowanceHistory(c, tx, l); err != nil {
err = errors.WithStack(err)
return
}
if aff != 1 {
err = fmt.Errorf("add change log faild")
return
}
s.dao.DelCouponAllowancesKey(c, cp.Mid, model.NotUsed)
s.dao.DelCouponAllowancesKey(c, cp.Mid, model.InUse)
return
}

View File

@@ -0,0 +1,107 @@
package service
import (
"go-common/app/admin/main/coupon/model"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
// go test -test.v -test.run TestAddAllowanceBatchInfo
func TestAddAllowanceBatchInfo(t *testing.T) {
Convey("TestAddAllowanceBatchInfo ", t, func() {
var err error
b := &model.CouponBatchInfo{
AppID: 1,
Name: "test1",
MaxCount: 1000,
CurrentCount: 1000,
StartTime: 1532057501,
ExpireTime: 1542057501,
Ver: 1,
Operator: "yubaihai",
LimitCount: 20,
FullAmount: 100,
Amount: 20,
State: 0,
CouponType: 3,
ExpireDay: 7,
PlatformLimit: "3,4",
}
_, err = s.AddAllowanceBatchInfo(c, b)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestUpdateAllowanceBatchInfo
func TestUpdateAllowanceBatchInfo(t *testing.T) {
Convey("TestUpdateAllowanceBatchInfo ", t, func() {
var err error
b := &model.CouponBatchInfo{
ID: 2,
AppID: 1,
Name: "test2",
MaxCount: 10000,
CurrentCount: 1000,
StartTime: 1532057501,
ExpireTime: 1542057501,
Ver: 1,
Operator: "yubaihai",
LimitCount: 200,
FullAmount: 100,
Amount: 20,
State: 0,
CouponType: 3,
PlatformLimit: "3",
}
err = s.UpdateAllowanceBatchInfo(c, b)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestUpdateBatchStatus
func TestUpdateBatchStatus(t *testing.T) {
Convey("TestUpdateBatchStatus ", t, func() {
So(s.UpdateBatchStatus(c, model.BatchStateNormal, "yubaihai", 150), ShouldBeNil)
})
}
// go test -test.v -test.run TestBatchInfo
func TestBatchInfo(t *testing.T) {
Convey("TestBatchInfo ", t, func() {
res, err := s.BatchInfo(c, "test2")
t.Logf("res(%v)", res)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestAllowanceSalary
func TestAllowanceSalary(t *testing.T) {
Convey("TestAllowanceSalary ", t, func() {
count, err := s.AllowanceSalary(c, nil, nil, []int64{332}, "allowance_test1", "vip")
time.Sleep(time.Second * 1)
t.Logf("count(%v)", count)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestUpdateAllowanceState
func TestUpdateAllowanceState(t *testing.T) {
Convey("TestUpdateAllowanceState ", t, func() {
err := s.UpdateAllowanceState(c, 1, model.NotUsed, "097060140820180713120943")
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestAllowanceList
func TestAllowanceList(t *testing.T) {
Convey("TestAllowanceList", t, func() {
res, err := s.AllowanceList(c, &model.ArgAllowanceSearch{
Mid: 1,
AppID: 0,
})
t.Logf("count(%v)", len(res))
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,137 @@
package service
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"math/rand"
"time"
"go-common/app/admin/main/coupon/model"
"go-common/library/ecode"
"go-common/library/log"
)
// CodePage code page.
func (s *Service) CodePage(c context.Context, a *model.ArgCouponCode) (res *model.CodePage, err error) {
res = new(model.CodePage)
var (
count int64
b *model.CouponBatchInfo
now = time.Now().Unix()
)
if count, err = s.dao.CountCode(c, a); err != nil {
return
}
if count <= 0 {
return
}
if res.CodeList, err = s.dao.CodeList(c, a); err != nil {
return
}
for _, v := range res.CodeList {
if b, err = s.BatchInfo(c, v.BatchToken); err != nil {
return
}
if b.ExpireDay == -1 && b.ExpireTime <= now {
v.State = model.CodeStateExpire
}
}
return
}
// CodeBlock code block.
func (s *Service) CodeBlock(c context.Context, a *model.ArgCouponCode) (err error) {
var code *model.CouponCode
if code, err = s.dao.CodeByID(c, a.ID); err != nil {
return
}
if code == nil || code.State != model.CodeStateNotUse {
return ecode.RequestErr
}
return s.dao.UpdateCodeBlock(c, &model.CouponCode{ID: a.ID, State: model.CodeStateBlock, Ver: code.Ver})
}
// CodeUnBlock code un block.
func (s *Service) CodeUnBlock(c context.Context, a *model.ArgCouponCode) (err error) {
var code *model.CouponCode
if code, err = s.dao.CodeByID(c, a.ID); err != nil {
return
}
if code == nil || code.State != model.CodeStateBlock {
return ecode.RequestErr
}
return s.dao.UpdateCodeBlock(c, &model.CouponCode{ID: a.ID, State: model.CodeStateNotUse, Ver: code.Ver})
}
// ExportCode export code.
func (s *Service) ExportCode(c context.Context, a *model.ArgCouponCode) (res []string, err error) {
if a.BatchToken == "" {
return nil, ecode.RequestErr
}
var cs []*model.CouponCode
a.Ps = 1000
a.Pn = 1
for {
if cs, err = s.dao.CodeList(context.Background(), a); err != nil {
return
}
if len(cs) == 0 {
break
}
for _, v := range cs {
res = append(res, v.Code)
}
a.Pn++
}
return
}
// InitCodes init codes
func (s *Service) InitCodes(c context.Context, token string) (err error) {
var info *model.CouponBatchInfo
if info, err = s.dao.BatchInfo(c, token); err != nil {
return
}
if info == nil {
return
}
if info.MaxCount == 0 || info.MaxCount > model.BatchCodeMaxCount {
return
}
// init code.
go func() {
log.Info("init code start arg[%s,%d]", token, info.MaxCount)
cs := []*model.CouponCode{}
for i := int64(0); i < info.MaxCount; i++ {
cs = append(cs, &model.CouponCode{
BatchToken: token,
State: model.CodeStateNotUse,
Code: codeToken(i),
CouponType: model.CouponAllowance,
})
if len(cs) == int(info.MaxCount) || len(cs)%model.BatchAddCodeSlice == 0 {
if err = s.dao.BatchAddCode(context.Background(), cs); err != nil {
log.Error("init code error[%s,%v]", token, err)
return
}
log.Info("init code ing arg[%s,%d,%d]", token, info.MaxCount, i)
time.Sleep(50 * time.Millisecond)
cs = []*model.CouponCode{}
}
}
log.Info("init code end arg[%s,%d]", token, info.MaxCount)
}()
return
}
func codeToken(i int64) string {
hash := md5.New()
unix := time.Now().UnixNano()
key := fmt.Sprintf("%v,%v,%v", unix, i, rand.Intn(100000000))
hash.Write([]byte(key))
sum := hash.Sum(nil)
code := hex.EncodeToString(sum)
return code[12:24]
}

View File

@@ -0,0 +1,33 @@
package service
import (
"testing"
"time"
"go-common/app/admin/main/coupon/model"
. "github.com/smartystreets/goconvey/convey"
)
// go test -test.v -test.run TestBatchInfo
func TestInitCodes(t *testing.T) {
Convey("TestInitCodes ", t, func() {
err := s.InitCodes(c, "allowance_batch100")
time.Sleep(2 * time.Second)
So(err, ShouldBeNil)
})
}
func TestCodeBlock(t *testing.T) {
Convey("TestCodeBlock ", t, func() {
err := s.CodeBlock(c, &model.ArgCouponCode{ID: 103})
So(err, ShouldBeNil)
})
}
func TestCodeUnBlock(t *testing.T) {
Convey("TestCodeUnBlock ", t, func() {
err := s.CodeUnBlock(c, &model.ArgCouponCode{ID: 103})
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,159 @@
package service
import (
"bytes"
"context"
"fmt"
"runtime/debug"
"time"
"go-common/app/admin/main/coupon/model"
col "go-common/app/service/main/coupon/model"
coumol "go-common/app/service/main/coupon/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
xtime "go-common/library/time"
"go-common/library/xstr"
"github.com/pkg/errors"
)
// AddBatchInfo add batch info.
func (s *Service) AddBatchInfo(c context.Context, b *model.CouponBatchInfo) (err error) {
if b.StartTime >= b.ExpireTime {
err = ecode.CouPonBatchTimeErr
return
}
b.BatchToken = s.token()
b.Ctime = xtime.Time(time.Now().Unix())
if _, err = s.dao.AddBatchInfo(c, b); err != nil {
err = errors.WithStack(err)
}
return
}
// BatchList batch list.
func (s *Service) BatchList(c context.Context, arg *model.ArgBatchList) (res []*model.CouponBatchResp, err error) {
var bs []*model.CouponBatchInfo
if bs, err = s.dao.BatchList(c, arg.AppID, arg.Type); err != nil {
err = errors.WithStack(err)
return
}
for _, v := range bs {
r := new(model.CouponBatchResp)
r.ID = v.ID
r.AppID = v.AppID
r.AppName = s.allAppInfo[v.AppID]
r.Name = v.Name
r.BatchToken = v.BatchToken
r.MaxCount = v.MaxCount
r.CurrentCount = v.CurrentCount
r.StartTime = v.StartTime
r.ExpireTime = v.ExpireTime
r.ExpireDay = v.ExpireDay
r.Operator = v.Operator
r.LimitCount = v.LimitCount
r.UseLimitExplain = model.NoLimitExplain
r.Amount = v.Amount
r.FullAmount = v.FullAmount
if r.PlatfromLimit, err = xstr.SplitInts(v.PlatformLimit); err != nil {
log.Error("xstr.SplitInts() err[%+v] ", v.PlatformLimit, err)
err = nil
}
if r.PlatfromLimit == nil {
r.PlatfromLimit = []int64{}
}
r.ProdLimExplainFmt(v.ProdLimMonth, v.ProdLimRenewal) //ProductLimitExplain
r.ProdLimMonth = v.ProdLimMonth
r.ProdLimRenewal = v.ProdLimRenewal
r.State = batchState(v)
res = append(res, r)
}
return
}
func batchState(v *model.CouponBatchInfo) (state int8) {
state = v.State
now := time.Now().Unix()
if v.ExpireDay == -1 {
if v.ExpireTime <= now {
state = model.CodeBatchExpire
}
}
return
}
// get coupon token
func (s *Service) token() string {
var b bytes.Buffer
b.WriteString(fmt.Sprintf("%07d", s.r.Int63n(9999999)))
b.WriteString(fmt.Sprintf("%03d", time.Now().UnixNano()/1e6%1000))
b.WriteString(time.Now().Format("20060102150405"))
return b.String()
}
// AllAppInfo app app info.
func (s *Service) AllAppInfo(c context.Context) map[int64]string {
return s.allAppInfo
}
// SalaryCoupon salary coupon.
func (s *Service) SalaryCoupon(c context.Context, mid int64, ct int64, count int, token string) (err error) {
arg := new(coumol.ArgSalaryCoupon)
arg.Count = count
arg.CouponType = ct
arg.Mid = mid
arg.Origin = model.SystemAdminSalary
arg.BatchToken = token
if err = s.couRPC.SalaryCoupon(c, arg); err != nil {
err = errors.WithStack(err)
}
return
}
//RunSalaryCoupon run salary coupon.
func (s *Service) RunSalaryCoupon(c context.Context, mids []int64, token string, appID int64, couponType int64, origin int64, mt string) {
go func() {
var (
err error
msgmids = []int64{}
)
defer func() {
if x := recover(); x != nil {
log.Error("RunSalaryCoupon.GoRun arg[%s] panic[%+v]", token, x)
log.Error("%s", debug.Stack())
}
}()
for _, v := range mids {
for i := 0; i < _maxretry; i++ {
if err = s.couRPC.SalaryCoupon(context.Background(), &col.ArgSalaryCoupon{
Mid: v,
CouponType: couponType,
Origin: origin,
Count: 1,
BatchToken: token,
AppID: appID,
}); err != nil {
time.Sleep(200 * time.Millisecond)
continue
}
break
}
if err != nil {
log.Error("RunSalaryCoupon faild arg[%s,%d] err[%+v] ", token, v, err)
continue
}
time.Sleep(10 * time.Millisecond)
log.Info("RunSalaryCoupon suc arg[%s,%d]", token, v)
msgmids = append(msgmids, v)
}
if len(mt) > 0 && s.c.Prop.SalaryNormalMsgOpen && len(msgmids) > 0 {
if cerr := s.msgchan.Do(c, func(c context.Context) {
s.sendMsg(msgmids, metadata.String(c, metadata.RemoteIP), 1, true, mt)
}); cerr != nil {
log.Error("s.sendMsg err(%+v)", cerr)
}
}
}()
}

View File

@@ -0,0 +1,133 @@
package service
import (
"context"
"flag"
"testing"
"time"
"go-common/app/admin/main/coupon/conf"
"go-common/app/admin/main/coupon/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
c = context.TODO()
s *Service
)
func init() {
var (
err error
)
flag.Set("conf", "../cmd/coupon-admin.toml")
if err = conf.Init(); err != nil {
panic(err)
}
c = context.Background()
if s == nil {
s = New(conf.Conf)
}
time.Sleep(time.Second)
}
// go test -test.v -test.run TestAddBatchInfo
func TestAddBatchInfo(t *testing.T) {
Convey("TestAddBatchInfo ", t, func() {
var err error
b := new(model.CouponBatchInfo)
b.AppID = int64(1)
b.Name = "name"
b.MaxCount = int64(100)
b.CurrentCount = int64(0)
b.StartTime = time.Now().Unix()
b.ExpireTime = time.Now().Unix() + int64(10000000)
err = s.AddBatchInfo(c, b)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestBatchList
func TestBatchList(t *testing.T) {
Convey("TestBatchList ", t, func() {
var err error
_, err = s.BatchList(c, &model.ArgBatchList{AppID: 1})
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestSalaryCoupon
func TestSalaryCoupon(t *testing.T) {
Convey("TestSalaryCoupon ", t, func() {
var (
err error
mid int64 = 1
ct int64 = 2
token = "test03"
count = 20
)
err = s.SalaryCoupon(c, mid, ct, count, token)
So(err, ShouldBeNil)
})
}
func TestService_CouponViewBatchAdd(t *testing.T) {
Convey("view batch add ", t, func() {
arg := new(model.ArgCouponViewBatch)
arg.AppID = 1
arg.Name = "观影券test"
arg.StartTime = time.Now().Unix()
arg.ExpireTime = time.Now().AddDate(0, 0, 12).Unix()
arg.Operator = "admin"
arg.MaxCount = -1
arg.LimitCount = -1
err := s.CouponViewBatchAdd(c, arg)
So(err, ShouldBeNil)
})
}
func TestService_CouponViewbatchSave(t *testing.T) {
Convey("view batch save", t, func() {
arg := new(model.ArgCouponViewBatch)
arg.ID = 47
arg.AppID = 1
arg.Name = "观影券test"
arg.StartTime = time.Now().Unix()
arg.ExpireTime = time.Now().AddDate(0, 0, 12).Unix()
arg.Operator = "admin"
arg.MaxCount = 100
arg.LimitCount = -1
err := s.CouponViewbatchSave(c, arg)
So(err, ShouldBeNil)
})
}
func TestService_CouponViewBlock(t *testing.T) {
Convey("coupon view block", t, func() {
mid := int64(39)
couponToken := "326209901520180419161313"
err := s.CouponViewBlock(c, mid, couponToken)
So(err, ShouldBeNil)
})
}
func TestService_CouponViewUnblock(t *testing.T) {
Convey("coupon view un block", t, func() {
mid := int64(39)
couponToken := "326209901520180419161313"
err := s.CouponViewUnblock(c, mid, couponToken)
So(err, ShouldBeNil)
})
}
func TestService_CouponViewList(t *testing.T) {
Convey("coupon view list", t, func() {
arg := new(model.ArgSearchCouponView)
arg.Mid = 39
arg.AppID = 1
res, count, err := s.CouponViewList(c, arg)
t.Logf("res:%+v count:%+v", res, count)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,96 @@
package service
import (
"context"
"math/rand"
"time"
"go-common/app/admin/main/coupon/conf"
"go-common/app/admin/main/coupon/dao"
"go-common/app/admin/main/coupon/model"
courpc "go-common/app/service/main/coupon/rpc/client"
"go-common/library/log"
"go-common/library/sync/pipeline/fanout"
)
const (
_maxSalaryCount = 100000
_notLimitSalary = -1
_maxretry = 3
_lockseconds = 604800
)
// Service struct
type Service struct {
c *conf.Config
dao *dao.Dao
r *rand.Rand
allAppInfo map[int64]string
couRPC *courpc.Service
// cache async del
cache *fanout.Fanout
// msg async send
msgchan *fanout.Fanout
}
// New init
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
r: rand.New(rand.NewSource(time.Now().Unix())),
allAppInfo: make(map[int64]string),
couRPC: courpc.New(c.RPCClient2.Coupon),
// cache chan
cache: fanout.New("cache", fanout.Worker(5), fanout.Buffer(1024)),
// msg chan
msgchan: fanout.New("cache", fanout.Worker(5), fanout.Buffer(10240)),
}
if err := s.loadappinfo(); err != nil {
panic(err)
}
go s.loadappinfoproc()
return s
}
// Ping Service
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.dao.Close()
}
func (s *Service) loadappinfo() (err error) {
var (
c = context.Background()
as []*model.AppInfo
)
if as, err = s.dao.AllAppInfo(c); err != nil {
log.Error("loadappinfo allappinfo error(%v)", err)
return
}
tmp := make(map[int64]string, len(as))
for _, v := range as {
tmp[v.ID] = v.Name
}
s.allAppInfo = tmp
log.Info("loadappinfo (%v) load success", tmp)
return
}
func (s *Service) loadappinfoproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.loadappinfoproc panic(%v)", x)
go s.loadappinfoproc()
log.Info("service.loadappinfoproc recover")
}
}()
for {
time.Sleep(time.Minute * 2)
s.loadappinfo()
}
}

View File

@@ -0,0 +1,211 @@
package service
import (
"context"
"mime/multipart"
"time"
"go-common/app/admin/main/coupon/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
//CouponViewBatchAdd view batch add.
func (s *Service) CouponViewBatchAdd(c context.Context, arg *model.ArgCouponViewBatch) (err error) {
if arg.StartTime > arg.ExpireTime {
err = ecode.CouPonBatchTimeErr
return
}
arg.BatchToken = s.token()
arg.CouponType = model.CouponVideo
if err = s.dao.AddViewBatch(c, arg); err != nil {
err = errors.WithStack(err)
}
return
}
//CouponViewbatchSave view batch save.
func (s *Service) CouponViewbatchSave(c context.Context, arg *model.ArgCouponViewBatch) (err error) {
if err = s.dao.UpdateViewBatch(c, arg); err != nil {
err = errors.WithStack(err)
}
return
}
//CouponViewBlock view block
func (s *Service) CouponViewBlock(c context.Context, mid int64, couponToken string) (err error) {
var (
r *model.CouponInfo
tx *sql.Tx
)
if tx, err = s.dao.BeginTran(c); err != nil {
err = errors.WithStack(err)
return
}
defer func() {
if err != nil {
tx.Rollback()
return
}
tx.Commit()
s.dao.DelCouponTypeCache(context.Background(), mid, model.CouponVideo)
}()
if r, err = s.dao.CouponViewInfo(c, couponToken, mid); err != nil {
err = errors.WithStack(err)
return
}
if r.State != model.NotUsed {
err = ecode.CouponInfoStateBlockErr
return
}
if err = s.dao.TxUpdateViewInfo(tx, model.Block, couponToken, mid); err != nil {
err = errors.WithStack(err)
return
}
olog := new(model.CouponChangeLog)
olog.Mid = mid
olog.CouponToken = couponToken
olog.State = model.Block
if err = s.dao.TxCouponViewLog(tx, olog); err != nil {
err = errors.WithStack(err)
}
return
}
//CouponViewUnblock view unblock
func (s *Service) CouponViewUnblock(c context.Context, mid int64, couponToken string) (err error) {
var (
r *model.CouponInfo
tx *sql.Tx
)
if tx, err = s.dao.BeginTran(c); err != nil {
err = errors.WithStack(err)
return
}
defer func() {
if err != nil {
tx.Rollback()
return
}
tx.Commit()
s.dao.DelCouponTypeCache(context.Background(), mid, model.CouponVideo)
}()
if r, err = s.dao.CouponViewInfo(c, couponToken, mid); err != nil {
err = errors.WithStack(err)
return
}
if r.State != model.Block {
err = ecode.CouponInfoStateUnblockErr
return
}
if err = s.dao.TxUpdateViewInfo(tx, model.NotUsed, couponToken, mid); err != nil {
err = errors.WithStack(err)
return
}
olog := new(model.CouponChangeLog)
olog.Mid = mid
olog.CouponToken = couponToken
olog.State = model.NotUsed
if err = s.dao.TxCouponViewLog(tx, olog); err != nil {
err = errors.WithStack(err)
}
return
}
//CouponViewList view list.
func (s *Service) CouponViewList(c context.Context, arg *model.ArgSearchCouponView) (res []*model.CouponInfo, count int64, err error) {
var (
bls []*model.CouponBatchInfo
)
if arg.AppID > 0 || len(arg.BatchToken) > 0 {
if bls, err = s.dao.BatchViewList(c, arg.AppID, arg.BatchToken, model.CouponVideo); err != nil {
err = errors.WithStack(err)
return
}
if len(bls) == 0 {
return
}
for _, v := range bls {
arg.BatchTokens = append(arg.BatchTokens, v.BatchToken)
}
}
if count, err = s.dao.SearchViewCouponCount(c, arg); err != nil {
err = errors.WithStack(err)
return
}
if count <= 0 {
return
}
if res, err = s.dao.SearchViewCouponInfo(c, arg); err != nil {
err = errors.WithStack(err)
return
}
now := time.Now()
for _, v := range res {
if v.OID == 0 {
v.Mtime = 0
} else {
var pgc *model.PGCInfoResq
if pgc, err = s.dao.GetPGCInfo(c, v.OID); err != nil {
log.Error("get pgc info coupon%+v error:%+v", v, err)
continue
}
if pgc != nil {
v.Title = pgc.Title
}
}
if len(v.BatchToken) > 0 {
var cbi *model.CouponBatchInfo
if cbi, err = s.dao.BatchInfo(c, v.BatchToken); err != nil {
err = errors.WithStack(err)
return
}
v.BatchName = cbi.Name
}
if v.ExpireTime < now.Unix() && v.State == model.NotUsed {
v.State = model.Expire
}
}
return
}
//CouponViewSalary view salary.
func (s *Service) CouponViewSalary(c context.Context, f multipart.File, h *multipart.FileHeader, mids []int64, token string) (count int, err error) {
var bi *model.CouponBatchInfo
if len(mids) == 0 {
if mids, err = s.ReadCsv(f, h); err != nil {
err = errors.WithStack(err)
return
}
}
if len(mids) == 0 {
err = ecode.CouponBatchSalaryCountZeroErr
return
}
if len(mids) > _maxSalaryCount {
err = ecode.CouponBatchSalaryLimitErr
return
}
if bi, err = s.dao.BatchInfo(c, token); err != nil {
err = errors.WithStack(err)
return
}
if bi == nil {
err = ecode.CouPonTokenNotFoundErr
return
}
if bi.State != model.BatchStateNormal {
err = ecode.CouPonHadBlockErr
return
}
if bi.MaxCount != _notLimitSalary && len(mids) > int(bi.MaxCount-bi.CurrentCount) {
err = ecode.CouponBatchSalaryLimitErr
return
}
s.RunSalaryCoupon(c, mids, token, bi.AppID, model.CouponVideo, model.SystemAdminSalary, "")
count = len(mids)
return
}