Create & Init Project...

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

View File

@@ -0,0 +1,6 @@
# v1.0.2
1. 修改缓存过期时间
# v1.0.1
1. 修复缓存key问题
# v1.0.0
1. 卡片一期

View File

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

View File

@@ -0,0 +1,14 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- yubaihai
- zhaogangtao
labels:
- main
- service
- service/main/card
options:
no_parent_owners: true
reviewers:
- yubaihai
- zhaogangtao

View File

@@ -0,0 +1,12 @@
# card-service
# 项目简介
1.卡片
# 编译环境
# 依赖包
# 编译执行

View File

@@ -0,0 +1,54 @@
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
proto_library(
name = "v1_proto",
srcs = ["api.proto"],
tags = ["automanaged"],
)
go_proto_library(
name = "v1_go_proto",
compilers = ["@io_bazel_rules_go//proto:go_grpc"],
importpath = "go-common/app/service/main/card/api/grpc/v1",
proto = ":v1_proto",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["client.go"],
embed = [":v1_go_proto"],
importpath = "go-common/app/service/main/card/api/grpc/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/net/rpc/warden:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_x_net//context: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"],
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,126 @@
syntax = "proto3";
// use {app_id}.{version} as package name
package card.service.v1;
option go_package = "v1";
message AllGroupReply {
ModelAllGroupResp res = 1;
}
message AllGroupReq {
int64 mid = 2;
}
message CardHotsReply {
repeated ModelCard data_0 = 1;
}
message CardHotsReq {
}
message CardReply {
ModelCard data_0 = 1;
}
message CardReq {
int64 id = 2;
}
message CardsByGidReply {
repeated ModelCard data_0 = 1;
}
message CardsByGidReq {
int64 gid = 2;
}
message DemountEquipReply {
}
message DemountEquipReq {
int64 mid = 2;
}
message EquipReply {
}
message EquipReq {
ModelArgEquip arg = 2;
}
message ModelAllGroupResp {
repeated ModelGroupInfo list = 1;
ModelUserCard user_card = 2;
}
message ModelArgEquip {
int64 mid = 1;
int64 card_id = 2;
}
message ModelCard {
int64 id = 1;
string name = 2;
int32 state = 3;
int32 deleted = 4;
int32 is_hot = 5;
string card_url = 6;
string big_card_url = 7;
int32 card_type = 8;
string card_type_name = 9;
}
message ModelGroupInfo {
int64 group_id = 1;
string group_name = 2;
repeated ModelCard cards = 3;
}
message ModelUserCard {
int64 mid = 1;
int64 id = 2;
string card_url = 3;
int32 card_type = 4;
string name = 5;
int64 expire_time = 6;
string card_type_name = 7;
string big_card_url = 8;
}
message UserCardReply {
ModelUserCard res = 1;
}
message UserCardReq {
int64 mid = 2;
}
message UserCardsReply {
map<int64, ModelUserCard> res = 1;
}
message UserCardsReq {
repeated int64 mids = 2;
}
service Card {
// Card get card info.
rpc Card(CardReq) returns(CardReply);
// CardHots get card hots.
rpc CardHots(CardHotsReq) returns(CardHotsReply);
// CardsByGid get card by gid.
rpc CardsByGid(CardsByGidReq) returns(CardsByGidReply);
// UserCard get user card info.
rpc UserCard(UserCardReq) returns(UserCardReply);
// UserCards get user card infos.
rpc UserCards(UserCardsReq) returns(UserCardsReply);
// Equip user equip card.
rpc Equip(EquipReq) returns(EquipReply);
// DemountEquip delete equip.
rpc DemountEquip(DemountEquipReq) returns(DemountEquipReply);
// AllGroup all group.
rpc AllGroup(AllGroupReq) returns(AllGroupReply);
}

View File

@@ -0,0 +1,25 @@
package v1
import (
fmt "fmt"
"go-common/library/net/rpc/warden"
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// AppID .
const AppID = "card.service"
// NewClient new grpc client
func NewClient(cfg *warden.ClientConfig, opts ...grpc.DialOption) (CardClient, error) {
client := warden.NewClient(cfg, opts...)
cc, err := client.Dial(context.Background(), fmt.Sprintf("discovery://default/%s", AppID))
if err != nil {
return nil, err
}
return NewCardClient(cc), nil
}
//${GOPATH}/bin/warden -name=Card -proto-package=account.card -dir service

View File

@@ -0,0 +1 @@
# HTTP API文档

View File

@@ -0,0 +1,44 @@
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 = ["test.toml"],
importpath = "go-common/app/service/main/card/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/card/conf:go_default_library",
"//app/service/main/card/server/grpc:go_default_library",
"//app/service/main/card/server/http:go_default_library",
"//app/service/main/card/service:go_default_library",
"//library/log:go_default_library",
"//library/net/trace: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,49 @@
package main
import (
"context"
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/service/main/card/conf"
grpc "go-common/app/service/main/card/server/grpc"
"go-common/app/service/main/card/server/http"
"go-common/app/service/main/card/service"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("card-service start")
trace.Init(conf.Conf.Tracer)
defer trace.Close()
svc := service.New(conf.Conf)
http.Init(conf.Conf, svc)
ws := grpc.New(conf.Conf.WardenServer, svc)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
svc.Close()
ws.Shutdown(context.Background())
log.Info("card-service exit")
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,44 @@
version = "3.0.0"
user = "nobody"
pid = "/tmp/card-service.pid"
dir = "./"
perf = "127.0.0.1:6070"
[bm]
addr = "0.0.0.0:7881"
maxListen = 1000
timeout = "1s"
[RPCClient2]
[mysql]
addr = "127.0.0.1:3306"
dsn = "root:123456@tcp(127.0.0.1:3306)/bilibili_card?timeout=3s&readTimeout=3s&writeTimeout=3s&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
[memcache]
name = "card-service"
proto = "tcp"
addr = "172.18.33.60:11234"
idle = 5
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "24h"
[wardenServer]
addr = "0.0.0.0:6077"
timeout = "1s"

View File

@@ -0,0 +1,40 @@
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/service/main/card/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/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace: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,99 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/memcache"
"go-common/library/conf"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/rpc"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config .
type Config struct {
Log *log.Config
BM *bm.ServerConfig
Tracer *trace.Config
Memcache *Memcache
MySQL *sql.Config
RPCClient2 *RPC
// grpc server
WardenServer *warden.ServerConfig
}
// RPC config
type RPC struct {
Vip *rpc.ClientConfig
}
// Memcache config
type Memcache struct {
*memcache.Config
CardExpire time.Duration
}
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.Toml2(); !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 = [
"cache.go",
"dao.cache.go",
"dao.go",
"mc.cache.go",
"mysql.go",
],
importpath = "go-common/app/service/main/card/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/card/conf:go_default_library",
"//app/service/main/card/model:go_default_library",
"//library/cache:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/golang.org/x/sync/singleflight: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 = [
"cache_test.go",
"dao.cache_test.go",
"dao_test.go",
"mc.cache_test.go",
"mysql_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/card/conf:go_default_library",
"//app/service/main/card/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,20 @@
package dao
import (
"context"
"strconv"
"go-common/app/service/main/card/model"
)
func (d *Dao) cacheSFEquip(id int64) string {
return strconv.FormatInt(id, 10)
}
//go:generate $GOPATH/src/go-common/app/tool/cache/gen
type _cache interface {
// cache: -batch=50 -max_group=10 -batch_err=continue -nullcache=&model.UserEquip{CardID:-1} -check_null_code=$!=nil&&$.CardID==-1
Equips(c context.Context, keys []int64) (map[int64]*model.UserEquip, error)
// cache: -nullcache=&model.UserEquip{CardID:-1} -check_null_code=$!=nil&&$.CardID==-1 -singleflight=true
Equip(c context.Context, key int64) (*model.UserEquip, error)
}

View File

@@ -0,0 +1,21 @@
package dao
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaocacheSFEquip(t *testing.T) {
convey.Convey("cacheSFEquip", t, func(ctx convey.C) {
var (
id = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.cacheSFEquip(id)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,164 @@
// Code generated by $GOPATH/src/go-common/app/tool/cache/gen. DO NOT EDIT.
/*
Package dao is a generated cache proxy package.
It is generated from:
type _cache interface {
// cache: -batch=50 -max_group=10 -batch_err=continue -nullcache=&model.UserEquip{CardID:-1} -check_null_code=$!=nil&&$.CardID==-1
Equips(c context.Context, keys []int64) (map[int64]*model.UserEquip, error)
// cache: -nullcache=&model.UserEquip{CardID:-1} -check_null_code=$!=nil&&$.CardID==-1 -singleflight=true
Equip(c context.Context, key int64) (*model.UserEquip, error)
}
*/
package dao
import (
"context"
"sync"
"go-common/app/service/main/card/model"
"go-common/library/net/metadata"
"go-common/library/stat/prom"
"go-common/library/sync/errgroup"
"golang.org/x/sync/singleflight"
)
var _ _cache
var cacheSingleFlights = [1]*singleflight.Group{{}}
// Equips get data from cache if miss will call source method, then add to cache.
func (d *Dao) Equips(c context.Context, keys []int64) (res map[int64]*model.UserEquip, err error) {
if len(keys) == 0 {
return
}
addCache := true
res, err = d.CacheEquips(c, keys)
if err != nil {
addCache = false
res = nil
err = nil
}
var miss []int64
for _, key := range keys {
if (res == nil) || (res[key] == nil) {
miss = append(miss, key)
}
}
prom.CacheHit.Add("Equips", int64(len(keys)-len(miss)))
defer func() {
for k, v := range res {
if v != nil && v.CardID == -1 {
delete(res, k)
}
}
}()
if len(miss) == 0 {
return
}
var missData map[int64]*model.UserEquip
missLen := len(miss)
prom.CacheMiss.Add("Equips", int64(missLen))
mutex := sync.Mutex{}
for i := 0; i < missLen; i += 50 * 10 {
var subKeys []int64
group := &errgroup.Group{}
ctx := c
if (i + 50*10) > missLen {
subKeys = miss[i:]
} else {
subKeys = miss[i : i+50*10]
}
missSubLen := len(subKeys)
for j := 0; j < missSubLen; j += 50 {
var ks []int64
if (j + 50) > missSubLen {
ks = subKeys[j:]
} else {
ks = subKeys[j : j+50]
}
group.Go(func() (err error) {
data, err := d.RawEquips(ctx, ks)
mutex.Lock()
for k, v := range data {
if missData == nil {
missData = make(map[int64]*model.UserEquip, len(keys))
}
missData[k] = v
}
mutex.Unlock()
return
})
}
err1 := group.Wait()
if err1 != nil {
err = err1
}
}
if res == nil {
res = make(map[int64]*model.UserEquip)
}
for k, v := range missData {
res[k] = v
}
if err != nil {
return
}
for _, key := range keys {
if res[key] == nil {
if missData == nil {
missData = make(map[int64]*model.UserEquip, len(keys))
}
missData[key] = &model.UserEquip{CardID: -1}
}
}
if !addCache {
return
}
d.cache.Save(func() {
d.AddCacheEquips(metadata.WithContext(c), missData)
})
return
}
// Equip get data from cache if miss will call source method, then add to cache.
func (d *Dao) Equip(c context.Context, id int64) (res *model.UserEquip, err error) {
addCache := true
res, err = d.CacheEquip(c, id)
if err != nil {
addCache = false
err = nil
}
defer func() {
if res != nil && res.CardID == -1 {
res = nil
}
}()
if res != nil {
prom.CacheHit.Incr("Equip")
return
}
var rr interface{}
sf := d.cacheSFEquip(id)
rr, err, _ = cacheSingleFlights[0].Do(sf, func() (r interface{}, e error) {
prom.CacheMiss.Incr("Equip")
r, e = d.RawEquip(c, id)
return
})
res = rr.(*model.UserEquip)
if err != nil {
return
}
miss := res
if miss == nil {
miss = &model.UserEquip{CardID: -1}
}
if !addCache {
return
}
d.cache.Save(func() {
d.AddCacheEquip(metadata.WithContext(c), id, miss)
})
return
}

View File

@@ -0,0 +1,40 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoEquips(t *testing.T) {
convey.Convey("Equips", t, func(ctx convey.C) {
var (
c = context.Background()
keys = []int64{1, 2, 3, 4}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Equips(c, keys)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoEquip(t *testing.T) {
convey.Convey("Equip", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(2)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Equip(c, id)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,51 @@
package dao
import (
"context"
"time"
"go-common/app/service/main/card/conf"
"go-common/library/cache"
"go-common/library/cache/memcache"
xsql "go-common/library/database/sql"
)
// Dao dao
type Dao struct {
c *conf.Config
// memcache
mc *memcache.Pool
mcExpire int32
// db
db *xsql.DB
// cache async save
cache *cache.Cache
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
// card memcache
mc: memcache.NewPool(c.Memcache.Config),
mcExpire: int32(time.Duration(c.Memcache.CardExpire) / time.Second),
db: xsql.NewMySQL(c.MySQL),
// cache chan
cache: cache.New(1, 1024),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.mc.Close()
d.db.Close()
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.pingMC(c); err != nil {
return
}
return d.db.Ping(c)
}

View File

@@ -0,0 +1,35 @@
package dao
import (
"flag"
"os"
"testing"
"go-common/app/service/main/card/conf"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "card-service")
flag.Set("conf_token", "e7ab64e6f5b4382145a90fdd470cb831")
flag.Set("tree_id", "59006")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
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/test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,159 @@
package dao
import (
"context"
"fmt"
"strconv"
"go-common/app/service/main/card/model"
mc "go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/stat/prom"
"github.com/pkg/errors"
)
const (
_prequip = "e_"
)
func equipKey(mid int64) string {
return _prequip + strconv.FormatInt(mid, 10)
}
// pingMC ping memcache.
func (d *Dao) pingMC(c context.Context) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
err = conn.Set(&mc.Item{
Key: "ping",
Value: []byte("pong"),
})
return
}
// CacheEquips get data from mc
func (d *Dao) CacheEquips(c context.Context, mids []int64) (res map[int64]*model.UserEquip, err error) {
keys := make([]string, 0, len(mids))
keyMidMap := make(map[string]int64, len(mids))
for _, mid := range mids {
key := equipKey(mid)
if _, ok := keyMidMap[key]; !ok {
// duplicate mid
keyMidMap[key] = mid
keys = append(keys, key)
}
}
conn := d.mc.Get(c)
defer conn.Close()
rs, err := conn.GetMulti(keys)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao equips cache")
return
}
res = make(map[int64]*model.UserEquip, len(mids))
for k, r := range rs {
e := &model.UserEquip{}
conn.Scan(r, e)
res[keyMidMap[k]] = e
}
return
}
// CacheEquip get user card equip from cache.
func (d *Dao) CacheEquip(c context.Context, mid int64) (v *model.UserEquip, err error) {
key := equipKey(mid)
conn := d.mc.Get(c)
defer conn.Close()
r, err := conn.Get(key)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao cache equip")
return
}
v = &model.UserEquip{}
if err = conn.Scan(r, v); err != nil {
err = errors.Wrap(err, "dao cache scan equip")
}
return
}
// AddCacheEquips Set data to mc
func (d *Dao) AddCacheEquips(c context.Context, values map[int64]*model.UserEquip) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
for k, v := range values {
item := &mc.Item{
Key: equipKey(k),
Object: v,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
}
if err = conn.Set(item); err != nil {
err = errors.Wrap(err, "dao add equips cache")
}
}
return
}
// AddCacheEquip set user card equip info into cache.
func (d *Dao) AddCacheEquip(c context.Context, mid int64, v *model.UserEquip) (err error) {
item := &mc.Item{
Key: equipKey(mid),
Object: v,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
}
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Set(item); err != nil {
err = errors.Wrap(err, "dao add equip cache")
}
return
}
// DelCacheEquips delete data from mc
func (d *Dao) DelCacheEquips(c context.Context, ids []int64) (err error) {
if len(ids) == 0 {
return
}
conn := d.mc.Get(c)
defer conn.Close()
for _, id := range ids {
key := equipKey(id)
if err = conn.Delete(key); err != nil {
if err == mc.ErrNotFound {
err = nil
continue
}
prom.BusinessErrCount.Incr("mc:DelCacheEquips")
log.Errorv(c, log.KV("DelCacheEquips", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
}
return
}
// DelCacheEquip delete data from mc
func (d *Dao) DelCacheEquip(c context.Context, id int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := equipKey(id)
if err = conn.Delete(key); err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:DelCacheEquip")
log.Errorv(c, log.KV("DelCacheEquip", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}

View File

@@ -0,0 +1,130 @@
package dao
import (
"context"
"testing"
"go-common/app/service/main/card/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoequipKey(t *testing.T) {
convey.Convey("equipKey", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := equipKey(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaopingMC(t *testing.T) {
convey.Convey("pingMC", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.pingMC(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoCacheEquips(t *testing.T) {
convey.Convey("CacheEquips", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{1, 2, 3}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.CacheEquips(c, mids)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoCacheEquip(t *testing.T) {
convey.Convey("CacheEquip", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(2)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.CacheEquip(c, mid)
ctx.Convey("Then err should be nil.v should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddCacheEquips(t *testing.T) {
convey.Convey("AddCacheEquips", t, func(ctx convey.C) {
var (
c = context.Background()
values map[int64]*model.UserEquip
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCacheEquips(c, values)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddCacheEquip(t *testing.T) {
convey.Convey("AddCacheEquip", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
v = &model.UserEquip{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCacheEquip(c, mid, v)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelCacheEquips(t *testing.T) {
convey.Convey("DelCacheEquips", t, func(ctx convey.C) {
var (
c = context.Background()
ids = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelCacheEquips(c, ids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelCacheEquip(t *testing.T) {
convey.Convey("DelCacheEquip", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelCacheEquip(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,121 @@
package dao
import (
"context"
"fmt"
"go-common/app/service/main/card/model"
xsql "go-common/library/database/sql"
"go-common/library/xstr"
"github.com/pkg/errors"
)
const (
_selUserEquip = "SELECT mid,card_id,expire_time FROM user_card_equip WHERE mid=? AND deleted = 0;"
_selUserEquips = "SELECT mid,card_id,expire_time FROM user_card_equip WHERE mid IN (%s) AND deleted = 0;"
_selEffectiveCard = "SELECT id,name,state,is_hot,card_url,big_crad_url,card_type,order_num,group_id,ctime,mtime FROM card_info WHERE state = 0 AND deleted = 0 ORDER BY order_num DESC;"
_selEffectiveCardGroup = "SELECT id,name,state,ctime,mtime,order_num FROM card_group WHERE state = 0 AND deleted = 0;"
_cardEquip = "INSERT INTO user_card_equip(mid,card_id,expire_time)VALUES(?,?,?) ON DUPLICATE KEY UPDATE card_id =?,expire_time=?,deleted=0;"
_cardDemount = "UPDATE user_card_equip SET deleted = 1 WHERE mid = ?;"
)
// RawEquip get user equip info.
func (d *Dao) RawEquip(c context.Context, mid int64) (r *model.UserEquip, err error) {
r = new(model.UserEquip)
row := d.db.QueryRow(c, _selUserEquip, mid)
if err = row.Scan(&r.Mid, &r.CardID, &r.ExpireTime); err != nil {
if err == xsql.ErrNoRows {
err = nil
r = nil
return
}
r = nil
err = errors.Wrapf(err, "dao equip mid(%d)", mid)
}
return
}
// RawEquips get user equip infos.
func (d *Dao) RawEquips(c context.Context, mids []int64) (res map[int64]*model.UserEquip, err error) {
var rows *xsql.Rows
res = make(map[int64]*model.UserEquip, len(mids))
midStr := xstr.JoinInts(mids)
if rows, err = d.db.Query(c, fmt.Sprintf(_selUserEquips, midStr)); err != nil {
err = errors.Wrapf(err, "dao equips mids(%s)", midStr)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.UserEquip)
if err = rows.Scan(&r.Mid, &r.CardID, &r.ExpireTime); err != nil {
err = errors.Wrapf(err, "dao equips scan mids(%s)", midStr)
res = nil
return
}
res[r.Mid] = r
}
err = rows.Err()
return
}
// EffectiveCards query all effective cards .
func (d *Dao) EffectiveCards(c context.Context) (res []*model.Card, err error) {
var rows *xsql.Rows
if rows, err = d.db.Query(c, _selEffectiveCard); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.Card)
if err = rows.Scan(&r.ID, &r.Name, &r.State, &r.IsHot, &r.CardURL, &r.BigCradURL, &r.CardType, &r.OrderNum,
&r.GroupID, &r.Ctime, &r.Mtime); err != nil {
err = errors.WithStack(err)
res = nil
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// EffectiveGroups query all effective groups .
func (d *Dao) EffectiveGroups(c context.Context) (res []*model.CardGroup, err error) {
var rows *xsql.Rows
if rows, err = d.db.Query(c, _selEffectiveCardGroup); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.CardGroup)
if err = rows.Scan(&r.ID, &r.Name, &r.State, &r.Ctime, &r.Mtime, &r.OrderNum); err != nil {
err = errors.WithStack(err)
res = nil
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// CardEquip card equip.
func (d *Dao) CardEquip(c context.Context, e *model.UserEquip) (err error) {
if _, err = d.db.Exec(c, _cardEquip, e.Mid, e.CardID, e.ExpireTime, e.CardID, e.ExpireTime); err != nil {
err = errors.WithStack(err)
return
}
return
}
// DeleteEquip delete card equip.
func (d *Dao) DeleteEquip(c context.Context, mid int64) (err error) {
if _, err = d.db.Exec(c, _cardDemount, mid); err != nil {
err = errors.WithStack(err)
return
}
return
}

View File

@@ -0,0 +1,102 @@
package dao
import (
"context"
"testing"
"go-common/app/service/main/card/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoRawEquip(t *testing.T) {
convey.Convey("RawEquip", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(2)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
r, err := d.RawEquip(c, mid)
ctx.Convey("Then err should be nil.r should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(r, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoRawEquips(t *testing.T) {
convey.Convey("RawEquips", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{1, 2, 3}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.RawEquips(c, mids)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoEffectiveCards(t *testing.T) {
convey.Convey("EffectiveCards", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.EffectiveCards(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoEffectiveGroups(t *testing.T) {
convey.Convey("EffectiveGroups", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.EffectiveGroups(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoCardEquip(t *testing.T) {
convey.Convey("CardEquip", t, func(ctx convey.C) {
var (
c = context.Background()
e = &model.UserEquip{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.CardEquip(c, e)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDeleteEquip(t *testing.T) {
convey.Convey("DeleteEquip", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DeleteEquip(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,60 @@
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
proto_library(
name = "model_proto",
srcs = ["model.proto"],
tags = ["automanaged"],
deps = ["@gogo_special_proto//github.com/gogo/protobuf/gogoproto"],
)
go_proto_library(
name = "model_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_proto"],
importpath = "go-common/app/service/main/card/model",
proto = ":model_proto",
tags = ["automanaged"],
deps = ["@com_github_gogo_protobuf//gogoproto:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"card.go",
"group.go",
"model.go",
"param.go",
],
embed = [":model_go_proto"],
importpath = "go-common/app/service/main/card/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/time:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto: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,33 @@
package model
import "go-common/library/time"
// Card info.
type Card struct {
ID int64 `json:"id"`
Name string `json:"name"`
State int32 `json:"state"`
Deleted int32 `json:"deleted"`
IsHot int32 `json:"is_hot"`
CardURL string `json:"card_url"`
BigCradURL string `json:"big_card_url"`
CardType int32 `json:"card_type"`
CardTypeName string `json:"card_type_name"`
OrderNum int64 `json:"order_num"`
GroupID int64 `json:"group_id"`
Operator string `json:"operator"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}
// UserCard user card info.
type UserCard struct {
Mid int64 `json:"mid"`
ID int64 `json:"id"`
CardURL string `json:"card_url"`
BigCradURL string `json:"big_card_url"`
CardType int32 `json:"card_type"`
Name string `json:"name"`
ExpireTime int64 `json:"expire_time"`
CardTypeName string `json:"card_type_name"`
}

View File

@@ -0,0 +1,31 @@
package model
import (
"go-common/library/time"
)
// CardGroup card group info.
type CardGroup struct {
ID int64 `json:"id"`
Name string `json:"name"`
State int8 `json:"state"`
Deleted int8 `json:"deleted"`
Operator string `json:"operator"`
OrderNum int64 `json:"order_num"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}
// AllGroupResp all group resp.
type AllGroupResp struct {
List []*GroupInfo `json:"list"`
UserCard *UserCard `json:"user_card,omitempty"`
}
// GroupInfo group info
type GroupInfo struct {
GroupID int64 `json:"group_id"`
GroupName string `json:"group_name"`
Cards []*Card `json:"cards"`
OrderNum int64 `json:"-"`
}

View File

@@ -0,0 +1,21 @@
package model
// card type
const (
CardTypeNone int32 = iota
CardTypeVip
CardTypeFree
)
// card is hot
const (
CardNotHot int32 = iota
CardIsHot
)
// CardTypeNameMap card name map.
var CardTypeNameMap = map[int32]string{
CardTypeNone: "",
CardTypeVip: "大会员",
CardTypeFree: "免费",
}

View File

@@ -0,0 +1,348 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: app/service/main/card/model/model.proto
/*
Package model is a generated protocol buffer package.
It is generated from these files:
app/service/main/card/model/model.proto
It has these top-level messages:
UserEquip
*/
package model
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type UserEquip struct {
Mid int64 `protobuf:"varint,1,opt,name=Mid,proto3" json:"mid"`
CardID int64 `protobuf:"varint,2,opt,name=CardID,proto3" json:"card_id"`
ExpireTime int64 `protobuf:"varint,3,opt,name=ExpireTime,proto3" json:"expire_time"`
}
func (m *UserEquip) Reset() { *m = UserEquip{} }
func (m *UserEquip) String() string { return proto.CompactTextString(m) }
func (*UserEquip) ProtoMessage() {}
func (*UserEquip) Descriptor() ([]byte, []int) { return fileDescriptorModel, []int{0} }
func init() {
proto.RegisterType((*UserEquip)(nil), "account.service.card.UserEquip")
}
func (m *UserEquip) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *UserEquip) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Mid != 0 {
dAtA[i] = 0x8
i++
i = encodeVarintModel(dAtA, i, uint64(m.Mid))
}
if m.CardID != 0 {
dAtA[i] = 0x10
i++
i = encodeVarintModel(dAtA, i, uint64(m.CardID))
}
if m.ExpireTime != 0 {
dAtA[i] = 0x18
i++
i = encodeVarintModel(dAtA, i, uint64(m.ExpireTime))
}
return i, nil
}
func encodeVarintModel(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *UserEquip) Size() (n int) {
var l int
_ = l
if m.Mid != 0 {
n += 1 + sovModel(uint64(m.Mid))
}
if m.CardID != 0 {
n += 1 + sovModel(uint64(m.CardID))
}
if m.ExpireTime != 0 {
n += 1 + sovModel(uint64(m.ExpireTime))
}
return n
}
func sovModel(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozModel(x uint64) (n int) {
return sovModel(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *UserEquip) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowModel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: UserEquip: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: UserEquip: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Mid", wireType)
}
m.Mid = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowModel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Mid |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CardID", wireType)
}
m.CardID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowModel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.CardID |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ExpireTime", wireType)
}
m.ExpireTime = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowModel
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ExpireTime |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipModel(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthModel
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipModel(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowModel
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowModel
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowModel
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthModel
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowModel
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipModel(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthModel = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowModel = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("app/service/main/card/model/model.proto", fileDescriptorModel) }
var fileDescriptorModel = []byte{
// 238 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4f, 0x2c, 0x28, 0xd0,
0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0xcf, 0x4d, 0xcc, 0xcc, 0xd3, 0x4f, 0x4e, 0x2c,
0x4a, 0xd1, 0xcf, 0xcd, 0x4f, 0x49, 0xcd, 0x81, 0x90, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42,
0x22, 0x89, 0xc9, 0xc9, 0xf9, 0xa5, 0x79, 0x25, 0x7a, 0x50, 0xc5, 0x7a, 0x20, 0x75, 0x52, 0xba,
0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0xe9, 0xf9, 0xfa,
0x60, 0xc5, 0x49, 0xa5, 0x69, 0x60, 0x1e, 0x98, 0x03, 0x66, 0x41, 0x0c, 0x51, 0xaa, 0xe1, 0xe2,
0x0c, 0x2d, 0x4e, 0x2d, 0x72, 0x2d, 0x2c, 0xcd, 0x2c, 0x10, 0x92, 0xe4, 0x62, 0xf6, 0xcd, 0x4c,
0x91, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x76, 0x62, 0x7f, 0x75, 0x4f, 0x9e, 0x39, 0x37, 0x33, 0x25,
0x08, 0x24, 0x26, 0xa4, 0xcc, 0xc5, 0xe6, 0x9c, 0x58, 0x94, 0xe2, 0xe9, 0x22, 0xc1, 0x04, 0x96,
0xe5, 0x7e, 0x75, 0x4f, 0x9e, 0x1d, 0x64, 0x61, 0x7c, 0x66, 0x4a, 0x10, 0x54, 0x4a, 0x48, 0x9f,
0x8b, 0xcb, 0xb5, 0xa2, 0x20, 0xb3, 0x28, 0x35, 0x24, 0x33, 0x37, 0x55, 0x82, 0x19, 0xac, 0x90,
0xff, 0xd5, 0x3d, 0x79, 0xee, 0x54, 0xb0, 0x68, 0x7c, 0x49, 0x66, 0x6e, 0x6a, 0x10, 0x92, 0x12,
0x27, 0xe9, 0x13, 0x0f, 0xe5, 0x18, 0x2e, 0x3c, 0x94, 0x63, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2,
0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0xa3, 0x58, 0xc1, 0xbe, 0x4c, 0x62, 0x03, 0xbb, 0xd0,
0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xe4, 0xac, 0x3c, 0x2e, 0x11, 0x01, 0x00, 0x00,
}

View File

@@ -0,0 +1,18 @@
syntax = "proto3";
package account.service.card;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "model";
option (gogoproto.goproto_enum_prefix_all) = false;
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
message UserEquip {
int64 Mid = 1 [(gogoproto.jsontag) = "mid"];
int64 CardID = 2 [(gogoproto.jsontag) = "card_id"];
int64 ExpireTime = 3 [(gogoproto.jsontag) = "expire_time"];
}

View File

@@ -0,0 +1,12 @@
package model
// ArgEquip card equip arg.
type ArgEquip struct {
Mid int64
CardID int64 `form:"id" validate:"required,min=1,gte=1"`
}
// ArgMids card mids arg.
type ArgMids struct {
Mids []int64 `form:"mids,split" validate:"min=1,max=50"`
}

View File

@@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["server.go"],
importpath = "go-common/app/service/main/card/server/grpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/card/api/grpc/v1:go_default_library",
"//app/service/main/card/model:go_default_library",
"//app/service/main/card/service:go_default_library",
"//library/net/rpc/warden: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,160 @@
// Package server generate by warden_gen
package server
import (
"context"
"go-common/app/service/main/card/api/grpc/v1"
"go-common/app/service/main/card/model"
service "go-common/app/service/main/card/service"
"go-common/library/net/rpc/warden"
)
// New Card warden rpc server
func New(c *warden.ServerConfig, svr *service.Service) *warden.Server {
ws := warden.NewServer(c)
v1.RegisterCardServer(ws.Server(), &server{svr})
ws, err := ws.Start()
if err != nil {
panic(err)
}
return ws
}
type server struct {
svr *service.Service
}
var _ v1.CardServer = &server{}
// Card get card info.
func (s *server) Card(c context.Context, req *v1.CardReq) (*v1.CardReply, error) {
var res *v1.ModelCard
if cd := s.svr.Card(c, req.Id); cd != nil {
res = convertModelCard(cd)
}
return &v1.CardReply{Data_0: res}, nil
}
// CardHots get card hots.
func (s *server) CardHots(c context.Context, req *v1.CardHotsReq) (*v1.CardHotsReply, error) {
cs := s.svr.CardHots(c)
ls := make([]*v1.ModelCard, len(cs))
for i, v := range cs {
ls[i] = convertModelCard(v)
}
return &v1.CardHotsReply{Data_0: ls}, nil
}
// CardsByGid get card by gid.
func (s *server) CardsByGid(c context.Context, req *v1.CardsByGidReq) (*v1.CardsByGidReply, error) {
cs := s.svr.CardsByGid(c, req.Gid)
ls := make([]*v1.ModelCard, len(cs))
for i, v := range cs {
ls[i] = convertModelCard(v)
}
return &v1.CardsByGidReply{Data_0: ls}, nil
}
// Equip user equip card.
func (s *server) Equip(c context.Context, req *v1.EquipReq) (*v1.EquipReply, error) {
return nil, s.svr.Equip(c, &model.ArgEquip{
Mid: req.Arg.Mid,
CardID: req.Arg.CardId,
})
}
// DemountEquip delete equip.
func (s *server) DemountEquip(c context.Context, req *v1.DemountEquipReq) (*v1.DemountEquipReply, error) {
return nil, s.svr.DemountEquip(c, req.Mid)
}
// AllGroup all group.
func (s *server) AllGroup(c context.Context, req *v1.AllGroupReq) (reply *v1.AllGroupReply, err error) {
var gs *model.AllGroupResp
if gs, err = s.svr.AllGroup(c, req.Mid); err != nil {
return
}
if gs == nil {
return
}
rs := new(v1.ModelAllGroupResp)
if gs.UserCard != nil {
rs.UserCard = convertModelUserCard(gs.UserCard)
}
ls := make([]*v1.ModelGroupInfo, len(gs.List))
for i, v := range gs.List {
cs := make([]*v1.ModelCard, len(v.Cards))
for ci, cv := range v.Cards {
cs[ci] = convertModelCard(cv)
}
ls[i] = &v1.ModelGroupInfo{
GroupId: v.GroupID,
GroupName: v.GroupName,
Cards: cs,
}
}
rs.List = ls
return &v1.AllGroupReply{
Res: rs,
}, nil
}
// UserCard get user card info.
func (s *server) UserCard(c context.Context, req *v1.UserCardReq) (res *v1.UserCardReply, err error) {
var cd *model.UserCard
res = new(v1.UserCardReply)
if cd, err = s.svr.UserCard(c, req.Mid); err != nil {
return
}
if cd == nil {
return
}
res.Res = convertModelUserCard(cd)
return
}
// UserCards get user card infos.
func (s *server) UserCards(c context.Context, req *v1.UserCardsReq) (res *v1.UserCardsReply, err error) {
var cs map[int64]*model.UserCard
res = new(v1.UserCardsReply)
if cs, err = s.svr.UserCards(c, req.Mids); err != nil {
return
}
if len(cs) <= 0 {
return
}
ls := make(map[int64]*v1.ModelUserCard, len(cs))
for k, v := range cs {
ls[k] = convertModelUserCard(v)
}
res.Res = ls
return
}
func convertModelUserCard(v *model.UserCard) *v1.ModelUserCard {
return &v1.ModelUserCard{
Mid: v.Mid,
Id: v.ID,
CardUrl: v.CardURL,
CardType: v.CardType,
Name: v.Name,
ExpireTime: v.ExpireTime,
CardTypeName: v.CardTypeName,
BigCardUrl: v.BigCradURL,
}
}
func convertModelCard(cv *model.Card) *v1.ModelCard {
return &v1.ModelCard{
Id: cv.ID,
Name: cv.Name,
State: cv.State,
Deleted: cv.Deleted,
IsHot: cv.IsHot,
CardUrl: cv.CardURL,
BigCardUrl: cv.BigCradURL,
CardType: cv.CardType,
CardTypeName: cv.CardTypeName,
}
}

View File

@@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"card.go",
"http.go",
],
importpath = "go-common/app/service/main/card/server/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/card/conf:go_default_library",
"//app/service/main/card/model:go_default_library",
"//app/service/main/card/service:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify: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,15 @@
package http
import (
"go-common/app/service/main/card/model"
bm "go-common/library/net/http/blademaster"
)
func byMids(c *bm.Context) {
var err error
arg := new(model.ArgMids)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(srv.UserCards(c, arg.Mids))
}

View File

@@ -0,0 +1,48 @@
package http
import (
"net/http"
"go-common/app/service/main/card/conf"
"go-common/app/service/main/card/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
srv *service.Service
verifySvc *verify.Verify
)
// Init init
func Init(c *conf.Config, svc *service.Service) {
srv = svc
verifySvc = verify.New(nil)
engine := bm.DefaultServer(c.BM)
route(engine)
if err := engine.Start(); err != nil {
log.Error("bm Start error(%v)", err)
panic(err)
}
}
func route(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
group := e.Group("/x/internal/card", verifySvc.Verify)
{
group.GET("/bymids", byMids)
}
}
func ping(c *bm.Context) {
if err := srv.Ping(c); err != nil {
log.Error("ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}

View File

@@ -0,0 +1,60 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"card.go",
"equip.go",
"group.go",
"service.go",
],
importpath = "go-common/app/service/main/card/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/card/conf:go_default_library",
"//app/service/main/card/dao:go_default_library",
"//app/service/main/card/model:go_default_library",
"//app/service/main/vip/model:go_default_library",
"//app/service/main/vip/rpc/client:go_default_library",
"//library/ecode: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"],
)
go_test(
name = "go_default_test",
srcs = [
"card_test.go",
"equip_test.go",
"group_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/card/conf:go_default_library",
"//app/service/main/card/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,77 @@
package service
import (
"context"
"sort"
"time"
"go-common/app/service/main/card/model"
"go-common/library/log"
)
// Card get card info.
func (s *Service) Card(c context.Context, id int64) *model.Card {
return s.cardmap[id]
}
// CardHots get card hots.
func (s *Service) CardHots(c context.Context) []*model.Card {
return s.cardhots
}
// CardsByGid get card by gid.
func (s *Service) CardsByGid(c context.Context, gid int64) []*model.Card {
return s.cardgidmap[gid]
}
func (s *Service) loadCard() (err error) {
var (
c = context.Background()
res []*model.Card
ok bool
)
if res, err = s.dao.EffectiveCards(c); err != nil {
return
}
tmp := make(map[int64]*model.Card, len(res))
htmp := []*model.Card{}
cgtmp := map[int64][]*model.Card{}
for _, v := range res {
if _, ok = s.cardgroupmap[v.GroupID]; !ok {
continue
}
v.CardTypeName = model.CardTypeNameMap[v.CardType]
tmp[v.ID] = v
if v.IsHot == model.CardIsHot {
htmp = append(htmp, v)
}
if len(cgtmp[v.GroupID]) == 0 {
cgtmp[v.GroupID] = []*model.Card{}
}
cgtmp[v.GroupID] = append(cgtmp[v.GroupID], v)
}
sort.Slice(htmp, func(i int, j int) bool {
return htmp[i].Mtime.Time().After(htmp[j].Mtime.Time())
})
s.cardhots = htmp
s.cardmap = tmp
s.cardgidmap = cgtmp
return
}
// loadcardproc load cards into memory.
func (s *Service) loadcardproc() {
defer func() {
if x := recover(); x != nil {
log.Error("loadbatchinfoproc panic(%v)", x)
go s.loadcardproc()
}
}()
for {
if err := s.loadCard(); err != nil {
time.Sleep(60 * time.Second)
continue
}
time.Sleep(5 * time.Minute)
}
}

View File

@@ -0,0 +1,59 @@
package service
import (
"context"
"flag"
"testing"
"time"
"go-common/app/service/main/card/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
c = context.TODO()
s *Service
)
func init() {
var (
err error
)
flag.Set("conf", "../cmd/test.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 TestCard
func TestCard(t *testing.T) {
Convey("TestCard ", t, func() {
card := s.Card(c, 1)
t.Logf("v(%v)", card)
So(card, ShouldNotBeEmpty)
})
}
// go test -test.v -test.run TestCardHots
func TestCardHots(t *testing.T) {
Convey("TestCardHots ", t, func() {
card := s.CardHots(c)
t.Logf("v(%v)", card)
So(card, ShouldNotBeEmpty)
})
}
// go test -test.v -test.run TestCardsByGid
func TestCardsByGid(t *testing.T) {
Convey("CardsByGid ", t, func() {
card := s.CardsByGid(c, 1)
t.Logf("v(%v)", card)
So(card, ShouldNotBeEmpty)
})
}

View File

@@ -0,0 +1,138 @@
package service
import (
"context"
"time"
"go-common/app/service/main/card/model"
vipmol "go-common/app/service/main/vip/model"
"go-common/library/ecode"
)
// UserCard get user card info.
func (s *Service) UserCard(c context.Context, mid int64) (res *model.UserCard, err error) {
if mid <= 0 {
return
}
var (
e *model.UserEquip
cd *model.Card
ok bool
now = time.Now().Unix()
)
if e, err = s.dao.Equip(c, mid); err != nil {
return
}
if ok, cd = s.checkEffective(e, now); !ok {
return
}
res = &model.UserCard{
Mid: mid,
ID: cd.ID,
CardURL: cd.CardURL,
BigCradURL: cd.BigCradURL,
CardType: cd.CardType,
Name: cd.Name,
ExpireTime: e.ExpireTime,
CardTypeName: model.CardTypeNameMap[cd.CardType],
}
return
}
// UserCards get user card infos.
func (s *Service) UserCards(c context.Context, mids []int64) (res map[int64]*model.UserCard, err error) {
if len(mids) <= 0 {
return
}
var (
es map[int64]*model.UserEquip
cd *model.Card
ok bool
now = time.Now().Unix()
)
if es, err = s.dao.Equips(c, mids); err != nil {
return
}
res = make(map[int64]*model.UserCard, len(es))
for _, e := range es {
if ok, cd = s.checkEffective(e, now); !ok {
continue
}
res[e.Mid] = &model.UserCard{
Mid: e.Mid,
ID: cd.ID,
CardURL: cd.CardURL,
CardType: cd.CardType,
Name: cd.Name,
ExpireTime: e.ExpireTime,
CardTypeName: model.CardTypeNameMap[cd.CardType],
}
}
return
}
// check card effective
func (s *Service) checkEffective(e *model.UserEquip, now int64) (b bool, cd *model.Card) {
var ok bool
if e == nil {
return
}
if cd, ok = s.cardmap[e.CardID]; !ok {
return
}
if cd.CardType == model.CardTypeVip && e.ExpireTime < now {
return
}
if _, ok = s.cardgroupmap[cd.GroupID]; !ok {
return
}
b = true
return
}
// Equip user equip card.
func (s *Service) Equip(c context.Context, arg *model.ArgEquip) (err error) {
var (
cd *model.Card
ok bool
)
if cd, ok = s.cardmap[arg.CardID]; !ok {
err = ecode.CardNotEffectiveErr
return
}
if _, ok = s.cardgroupmap[cd.GroupID]; !ok {
err = ecode.CardNotEffectiveErr
return
}
e := new(model.UserEquip)
e.CardID = arg.CardID
e.Mid = arg.Mid
if cd.CardType == model.CardTypeVip {
var v *vipmol.VipInfoResp
if v, err = s.vipRPC.VipInfo(c, &vipmol.ArgRPCMid{Mid: arg.Mid}); err != nil {
return
}
if v == nil || v.VipStatus == vipmol.Expire || v.VipType == int8(vipmol.NotVip) {
err = ecode.CardEquipNotVipErr
return
}
e.ExpireTime = v.VipDueDate
}
if err = s.dao.CardEquip(c, e); err != nil {
return
}
s.dao.DelCacheEquip(c, e.Mid)
return
}
// DemountEquip delete equip.
func (s *Service) DemountEquip(c context.Context, mid int64) (err error) {
if mid <= 0 {
return
}
if err = s.dao.DeleteEquip(c, mid); err != nil {
return
}
s.dao.DelCacheEquip(c, mid)
return
}

View File

@@ -0,0 +1,45 @@
package service
import (
"testing"
"time"
"go-common/app/service/main/card/model"
. "github.com/smartystreets/goconvey/convey"
)
// go test -test.v -test.run TestUserCard
func TestUserCard(t *testing.T) {
Convey("TestUserCard ", t, func() {
card, err := s.UserCard(c, 1)
t.Logf("v(%v)", card)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestUserCards
func TestUserCards(t *testing.T) {
Convey("TestUserCards ", t, func() {
card, err := s.UserCards(c, []int64{977771, 977772, 977773})
time.Sleep(1 * time.Second)
t.Logf("v(%v)", card)
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestEquip
func TestEquip(t *testing.T) {
Convey("TestEquip ", t, func() {
err := s.Equip(c, &model.ArgEquip{Mid: 27515232, CardID: 1})
So(err, ShouldBeNil)
})
}
// go test -test.v -test.run TestDemountEquip
func TestDemountEquip(t *testing.T) {
Convey("TestDemountEquip ", t, func() {
err := s.DemountEquip(c, 27515232)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,65 @@
package service
import (
"context"
"sort"
"time"
"go-common/app/service/main/card/model"
"go-common/library/log"
)
// AllGroup all group.
func (s *Service) AllGroup(c context.Context, mid int64) (res *model.AllGroupResp, err error) {
res = new(model.AllGroupResp)
if res.UserCard, err = s.UserCard(c, mid); err != nil {
return
}
ls := []*model.GroupInfo{}
for _, v := range s.cardgroupmap {
ls = append(ls, &model.GroupInfo{
GroupID: v.ID,
GroupName: v.Name,
OrderNum: v.OrderNum,
Cards: s.cardgidmap[v.ID],
})
}
sort.Slice(ls, func(i int, j int) bool {
return ls[i].OrderNum > ls[j].OrderNum
})
res.List = ls
return
}
func (s *Service) loadGroup() (err error) {
var (
c = context.Background()
res []*model.CardGroup
)
if res, err = s.dao.EffectiveGroups(c); err != nil {
return
}
tmp := make(map[int64]*model.CardGroup, len(res))
for _, v := range res {
tmp[v.ID] = v
}
s.cardgroupmap = tmp
return
}
// loadcardgroupproc load cards into memory.
func (s *Service) loadcardgroupproc() {
defer func() {
if x := recover(); x != nil {
log.Error("loadcardgroupproc panic(%v)", x)
go s.loadcardproc()
}
}()
for {
if err := s.loadGroup(); err != nil {
time.Sleep(60 * time.Second)
continue
}
time.Sleep(5 * time.Minute)
}
}

View File

@@ -0,0 +1,16 @@
package service
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
// go test -test.v -test.run TestAllGroup
func TestAllGroup(t *testing.T) {
Convey("TestAllGroup ", t, func() {
card, err := s.AllGroup(c, 1)
t.Logf("v(%v)", card)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,55 @@
package service
import (
"context"
"go-common/app/service/main/card/conf"
"go-common/app/service/main/card/dao"
"go-common/app/service/main/card/model"
viprpc "go-common/app/service/main/vip/rpc/client"
)
// Service struct
type Service struct {
c *conf.Config
dao *dao.Dao
cardmap map[int64]*model.Card
cardgidmap map[int64][]*model.Card
cardhots []*model.Card
cardgroupmap map[int64]*model.CardGroup
// vip rpc
vipRPC *viprpc.Service
}
// New init
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
// cache chan
cardmap: make(map[int64]*model.Card),
cardgidmap: make(map[int64][]*model.Card),
cardgroupmap: make(map[int64]*model.CardGroup),
cardhots: []*model.Card{},
vipRPC: viprpc.New(c.RPCClient2.Vip),
}
if err := s.loadGroup(); err != nil {
panic(err)
}
if err := s.loadCard(); err != nil {
panic(err)
}
go s.loadcardproc()
go s.loadcardgroupproc()
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()
}