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,54 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
package(default_visibility = ["//visibility:public"])
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"distrib.go",
"model.go",
"order.go",
"pay.go",
"promo.go",
"stock.go",
"ticket.go",
],
importpath = "go-common/app/service/openplatform/ticket-sales/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/common/openplatform/encoding:go_default_library",
"//app/service/openplatform/ticket-sales/api/grpc/type:go_default_library",
"//app/service/openplatform/ticket-sales/api/grpc/v1:go_default_library",
"//library/log:go_default_library",
"//library/time:go_default_library",
"@com_github_gogo_protobuf//types:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/openplatform/ticket-sales/model/consts:all-srcs",
"//app/service/openplatform/ticket-sales/model/order_checker/account:all-srcs",
"//app/service/openplatform/ticket-sales/model/order_checker/item:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,47 @@
package model
//缓存key的常量
const (
CacheKeyOrderList = "order_ls" //订单列表缓存
CacheKeyOrderMn = "order_mn" //order_main缓存
CacheKeyOrderCnt = "order_cnt" //订单数缓存
CacheKeyOrderDt = "order_dt" //order_detail缓存
CacheKeyOrderSKU = "order_sku" //order_sku缓存
CacheKeyOrderPayCh = "order_ch" //order_pay_charge缓存
CacheKeyStock = "stock:%d" // 库存数 redis key 前缀
CacheKeyStockL = "locked:%d" // 锁定库存数 redisKey 前缀
CacheKeySku = "sku:%d" // skuId => sku redis key 前缀
CacheKeyItemSku = "sku.item:%d" // itemId => sku redis key 前缀
// 票相关 key
CacheKeyScreenSales = "ticket:screen.sales" // hash {sid:cnt} 各场次总的出票数
CacheKeyScreenDailySales = "ticket:screen.daily" // hash {sid:cnt} 各场次的当日销量
CacheKeyUserBuyScreen = "ticket:user.screen:%d" // hash {sid:cnt} 用户购买各场次票数量
CacheKeyOrderTickets = "ticket:order.tks:%d" // 一笔订单下所有电子票信息
CacheKeyScreenTickets = "ticket:screens.tks:%d:%d" // 一个场次下用户电子票信息
CacheKeyTicketQr = "ticket:qr.tk:%s" // 一个二维码对应电子票信息
CacheKeyTicket = "ticket:tk:%d" // 单张电子票信息
CacheKeyTicketPool = "ticket:pool:%d" // sku票池
CacheKeyTicketSend = "ticket:send:%d" // 票的赠送信息 send_tid => ticket_send
CacheKeyTicketRecv = "ticket:recv:%d" // 票的赠送信息 recv_tid => ticket_send
RedisExpireStock = 120 // 库存量缓存过期时间
RedisExpireStockTmp = 2 // 库存量缓存过期时间
RedisExpireSku = 1800 // SKU信息缓存过期时间
RedisExpireSkuTmp = 2 // SKU信息缓存过期时间
RedisExpireTenMin = 600 // 过期时间 10 分钟
RedisExpireTenMinTmp = 2 // 过期时间 10 分钟
RedisExpireOneDay = 86400 // 过期时间 1 天
RedisExpireOneDayTmp = 2 // 过期时间 1 天 兼容版
CacheKeyPromo = "%d:promotion:sales" //拼团活动缓存
CacheKeyPromoGroup = "%d:promotion:group:sales" //团缓存
CacheKeyPromoOrder = "%d:promotion:order:sales" //拼团订单缓存
CacheKeyPromoOrders = "%d:promotion:orders:sales" //拼团团订单缓存
RedisExpirePromo = 1 // 过期时间5分钟 to do
RedisExpirePromoGroup = 1 // 过期时间5分钟 to do
RedisExpirePromoOrder = 1 // 过期时间5分钟 to do
RedisExpirePromoOrders = 1 // 过期时间5分钟 to do
)

View File

@@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"order.go",
"pay.go",
"project.go",
"promo.go",
"stock.go",
"ticket.go",
],
importpath = "go-common/app/service/openplatform/ticket-sales/model/consts",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
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,41 @@
package consts
//订单常量
const (
OrderTypeNormal = 1 //普通订单
OrderTypeGroup = 2 //拼团订单
OrderTypeDistrib = 4 //分销订单(已废弃)
OrderStatusUnpaid = 1 //未付款
OrderStatusPaid = 2 //已付款
OrderStatusRefunded = 3 //已退款
OrderStatusCancel = 4 //已取消
SubStatusUnpaid = 1 //未付款
SubStatusClose = 2 //已取消-系统关闭
SubStatusPaid = 3 //已付款-待出票
//4-6为退款状态已拆出为refundStatus7-8为已废弃付款子状态
SubStatusCompleted = 9 //已完成
SubStatusUnshipped = 10 //已待发货
SubStatusShipped = 11 //已付款-待收货
SubStatusCancel = 12 //已取消-用户取消(待启用)
RefundStatusNone = 0 //无退款
RefundStatusPtRefunding = 1 //部分退款中
RefundStatusPtRefunded = 2 //部分已退款
RefundStatusRefunding = 3 //退款中
RefundStatusRefunded = 4 //已退款
RefundTxStatusCreated = 1 //退款流水创建
RefundTxStatusSucc = 2 //退款流水处理成功
)
var (
//OrderTypes 订单类型
OrderTypes = map[int16]string{
OrderTypeNormal: "普通",
OrderTypeGroup: "拼团",
//Deprecated
OrderTypeDistrib: "分销",
}
)

View File

@@ -0,0 +1,11 @@
package consts
// 支付状态
const (
PayStatusPaying = "PAYING" // 待支付
PayStatusOverdue = "OVERDUE" // 已过期
PayStatusClose = "CLOSED" // 支付关闭
PayStatusFail = "FAIL" // 支付失败
PayStatusSuccess = "SUCCESS" // 支付成功
PayStatusFinished = "FINISHED" // 交易成功
)

View File

@@ -0,0 +1,22 @@
package consts
//项目相关状态
const (
DefaultBuyNumLimit = 8 //默认单张订单购买限制
BuyerInfoTel = 1 //需要手机号
BuyerInfoPerID = 2 //需要身份证号
BuyerInfoPerPic = 3 //需要身份证图片(未实现)
DeliverTypeNone = 1 //不配送
DeliverTypeSelf = 2 //自取
DeliverTypeExpress = 3 //快递配送
TicketTypePaper = 1 //纸质票
TicketTypeElec = 2 //电子票
TicketTypeExt = 3 //外部票(电子票)
TicketTypeExch = 12 //兑换票(未实现,实为纸质票+自取)
PickSeatYes = 1 //选座项目
PickSeatNo = 0 //不选座项目
)

View File

@@ -0,0 +1,21 @@
package consts
//拼团常量
const (
PromoWaitShelf int16 = 1 //待上架
PromoUpShelf int16 = 2 //已上架
PromoDelShelf int16 = 3 //废弃
PromoFinishShelf int16 = 4 //已结束
DoUpShelf int16 = 1 //上架
DoDelShelf int16 = 2 //废弃
GroupDoing int16 = 0 //拼团中
GroupSuccess int16 = 1 //拼团成功
GroupFailed int16 = 2 //拼团失败
PromoOrderUnpaid int16 = 1 //待支付
PromoOrderPaid int16 = 2 //已支付
PromoOrderRefund int16 = 3 //已退款
PromoOrderCancel int16 = 4 //已取消
)

View File

@@ -0,0 +1,16 @@
package consts
// 库存操作日志 opType 常量
const (
OpTypeOrder int16 = 0 // 0 下单减库存
OpTypePaid int16 = 1 // 1 订单支付解锁库存
OpTypeRefund int16 = 2 // 退票补充库存(3为退 voucher 减库存,已弃用)
OpTypeReserve int16 = 4 // 预留扣库存
OpTypeConfirm int16 = 5 // 确认预留解锁库存
OpTypeActive int16 = 7 // 场次激活重置库存
OpTypeBaseDecr int16 = 10 // 基础库存减少
OpTypeBaseIncr int16 = 11 // 基础库存增加
OpTypePrivDecr int16 = 12 // 活动库存减少
OpTypePrivIncr int16 = 13 // 活动库存增加
OpTypePrivInit int16 = 14 // 活动库存初始化
)

View File

@@ -0,0 +1,26 @@
package consts
//票状态常量
const (
TkStatusUnchecked = 0 //未检票
TkStatusChecked = 1 //已检票
TkStatusExpired = 2 //已过期
TkStatusRefunded = 3 //已退票
TkStatusRefunding = 4 //退票中
TkStatusRefundFail = 5 //退票失败
TkStatusSended = 6 //已转赠
)
// 票类型: 0-订单出票, 1-系统赠票, 2-用户赠票, 3-票代分销'
const (
TkTypeOrder = 0 // 订单出票
TkTypeSystemSend = 1 // 系统赠票
TkTypeUserSend = 2 // 用户赠票
TkTypeDistrib = 3 // 票代分销
)
// ticket_id 类型
const (
TIDTypeSend = "send"
TIDTypeRecv = "recv"
)

View File

@@ -0,0 +1,23 @@
package model
//DistOrderArg 分销订单同步入参
type DistOrderArg struct {
Oid uint64 `form:"oid" validate:"required"`
CmAmount uint64 `form:"cm_amount" validate:"min=1,required"`
CmMethod int64 `form:"cm_method" validate:"min=1,required"`
CmPrice uint64 `form:"cm_price" validate:"min=1,required"`
Duid uint64 `form:"dist_user" validate:"min=0"`
Stat int64 `form:"status" validate:"min=0"`
Pid uint64 `form:"pid" validate:"min=0"`
Count uint64 `form:"count" validate:"min=1,required"`
Sid uint64 `form:"sid" validate:"min=0"`
Type int64 `form:"type" validate:"min=0"`
RefStat int64 `form:"refund_status" validate:"min=0"`
PayAmount uint64 `form:"payment_amount" validate:"min=0"`
Serial string `form:"serial" validate:"required"`
}
//DistOrderGetArg 分销订单查询入参
type DistOrderGetArg struct {
Oid uint64 `form:"oid" validate:"required"`
}

View File

@@ -0,0 +1,87 @@
package model
import (
"fmt"
"strings"
"go-common/library/time"
)
//Order...主订单表状态
const (
OrderPaid = 2
OrderRefunded = 3
OrderRefundPartly = 2
OrderRefundedAll = 4
)
//DistOrder...分销订单表状态
const (
DistOrderNormal = 1
DistOrderRefunded = 2
DistOrderPartlyRefunded = 3
)
// SpecsSeparator sku 规格分隔符
const SpecsSeparator = "_"
//OrderInfo 订单同步字段
type OrderInfo struct {
Oid uint64 `json:"oid"`
CmAmount uint64 `json:"cm_amount"`
CmMethod int64 `json:"cm_method"`
CmPrice uint64 `json:"cm_price"`
Duid uint64 `json:"duid"`
Stat int64 `json:"status"`
Pid uint64 `json:"pid"`
Count uint64 `json:"count"`
Sid uint64 `json:"sid"`
Type int64 `json:"type"`
PayAmount uint64 `json:"pay_amount"`
Serial string `json:"serial_num"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}
//OrderStockCnt 订单库存
type OrderStockCnt struct {
OrderID int64
Count int64
}
//SkuCnt sku库存
type SkuCnt struct {
SkuID int64
Count int64
}
//Batch 批量库存操作
type Batch struct {
ScreenID int64 // 场次 ID
TicketPriceID int64 // 票价 ID 就是 sku_stock SKUID
SkAlert int64 // 库存预警数
TotalStock int64 // 总库存数
}
//Specs 生成库存规格
func (b *Batch) Specs() (s string) {
s = fmt.Sprintf("%d%s%d", b.ScreenID, SpecsSeparator, b.TicketPriceID)
return
}
//InsPlHlds insert语句占位符
func InsPlHlds(colCnt int, rowCnt int) string {
hLen := colCnt*2 + 2
h := make([]byte, hLen)
h[0] = '('
h[hLen-2] = ')'
h[hLen-1] = ','
copy(h[1:], []byte(strings.Repeat(",?", colCnt)[1:]))
bPlHlds := make([]byte, hLen*rowCnt-1)
j := 0
for i := 0; i < rowCnt; i++ {
copy(bPlHlds[j:], h)
j += hLen
}
return string(bPlHlds)
}

View File

@@ -0,0 +1,469 @@
package model
import (
"encoding/json"
"go-common/app/common/openplatform/encoding"
"go-common/app/service/openplatform/ticket-sales/api/grpc/type"
"go-common/app/service/openplatform/ticket-sales/api/grpc/v1"
"go-common/library/time"
"github.com/gogo/protobuf/types"
)
//OrderMain 订单主表结构
type OrderMain struct {
OrderID int64 `json:"order_id"`
UID string `json:"uid"`
OrderType int16 `json:"order_type"`
ItemID int64 `json:"item_id"`
ItemInfo *_type.OrderItemInfo `json:"item_info"`
Count int64 `json:"count"`
TotalMoney int64 `json:"total_money"`
PayMoney int64 `json:"pay_money"`
ExpressFee int64 `json:"express_fee"`
PayChannel int16 `json:"pay_channel"`
PayTime int64 `json:"pay_time"`
Source string `json:"source"`
Status int16 `json:"status"`
SubStatus int16 `json:"sub_status"`
RefundStatus int16 `json:"refund_status"`
IsDeleted int16 `json:"is_deleted"`
CTime time.Time `json:"ctime"`
MTime time.Time `json:"mtime"`
}
//OrderMainQuerier 订单表查询参数
type OrderMainQuerier v1.ListOrdersRequest
// OrderDetail 订单详情表信息
type OrderDetail struct {
OrderID int64 `json:"order_id"`
Buyer string `json:"buyer"`
Tel string `json:"tel"`
PersonalID string `json:"personal_id"`
ExpressCO string `json:"express_co"`
ExpressNO string `json:"express_no"`
ExpressType int16 `json:"express_type"`
Remark string `json:"remark"`
DeviceType int16 `json:"device_type"`
IP []byte `json:"ip"`
Coupon *_type.OrderCoupon `json:"coupon"`
DeliverDetail *_type.OrderDeliver `json:"deliver_detail"`
Detail *_type.OrderExtra `json:"detail"`
MSource string `json:"msource"`
CTime time.Time `json:"-"`
MTime time.Time `json:"-"`
}
//OrderSKU order_sku表结构
type OrderSKU _type.OrderSKU
//OrderPayCharge 订单支付表结构
type OrderPayCharge _type.OrderPayCharge
//GetFields 获取order_main表所有字段,参数是需要排除的字段
func (o *OrderMain) GetFields(except *types.FieldMask) []string {
fields := []string{
"order_id", "uid", "order_type", "item_id", "item_info",
"count", "total_money", "express_fee", "pay_money", "pay_channel",
"pay_time", "source", "status", "sub_status", "refund_status",
"is_deleted", "ctime", "mtime",
}
if except != nil {
lp := len(except.Paths)
mExcept := make(map[string]bool, lp)
for _, v := range except.Paths {
mExcept[v] = true
}
res := make([]string, len(fields)-lp)
i := 0
for _, v := range fields {
if ok := mExcept[v]; !ok {
res[i] = v
i++
}
}
return res
}
return fields
}
//GetFields 获取order_detail字段名称
func (o *OrderDetail) GetFields(except *types.FieldMask) []string {
fields := []string{
"order_id", "buyer", "tel", "personal_id", "express_co",
"express_no", "express_type", "remark", "device_type", "ip",
"coupon", "deliver_detail", "detail", "msource", "ctime",
"mtime",
}
if except != nil {
lp := len(except.Paths)
mExcept := make(map[string]bool, lp)
for _, v := range except.Paths {
mExcept[v] = true
}
res := make([]string, len(fields)-lp)
i := 0
for _, v := range fields {
if ok := mExcept[v]; !ok {
res[i] = v
i++
}
}
return res
}
return fields
}
//GetFields 获取order_sku对象的字段名
func (o *OrderSKU) GetFields(except *types.FieldMask) []string {
fields := []string{
"order_id", "sku_id", "count", "origin_price", "price",
"seat_ids", "ticket_type", "discounts", "ctime", "mtime",
}
if except != nil {
lp := len(except.Paths)
mExcept := make(map[string]bool, lp)
for _, v := range except.Paths {
mExcept[v] = true
}
res := make([]string, len(fields)-lp)
i := 0
for _, v := range fields {
if ok := mExcept[v]; !ok {
res[i] = v
i++
}
}
return res
}
return fields
}
//GetFields 获取order_pay_charge对象的字段名
func (o *OrderPayCharge) GetFields(except *types.FieldMask) []string {
fields := []string{
"order_id", "charge_id", "channel", "paid", "refunded",
"ctime", "mtime",
}
if except != nil {
lp := len(except.Paths)
mExcept := make(map[string]bool, lp)
for _, v := range except.Paths {
mExcept[v] = true
}
res := make([]string, len(fields)-lp)
i := 0
for _, v := range fields {
if ok := mExcept[v]; !ok {
res[i] = v
i++
}
}
return res
}
return fields
}
//GetPtrs 获取order_main对象指针
// 如果设置vptr参数,会把struct指针替换成string指针,并在vptr保存原struct指针(as value)和它在返回数组中的下标(as key)
func (o *OrderMain) GetPtrs(fields *types.FieldMask, vptr map[int]interface{}) []interface{} {
ptrs := map[string]interface{}{
"order_id": &o.OrderID,
"uid": &o.UID,
"order_type": &o.OrderType,
"item_id": &o.ItemID,
"item_info": &o.ItemInfo,
"count": &o.Count,
"total_money": &o.TotalMoney,
"express_fee": &o.ExpressFee,
"pay_money": &o.PayMoney,
"pay_channel": &o.PayChannel,
"pay_time": &o.PayTime,
"source": &o.Source,
"status": &o.Status,
"sub_status": &o.SubStatus,
"refund_status": &o.RefundStatus,
"is_deleted": &o.IsDeleted,
"ctime": &o.CTime,
"mtime": &o.MTime,
}
if fields == nil {
fields = &types.FieldMask{Paths: o.GetFields(nil)}
}
ret := make([]interface{}, len(fields.Paths))
i := 0
for _, f := range fields.Paths {
if vptr != nil && f == "item_info" {
var s string
ret[i] = &s
vptr[i] = ptrs[f]
} else {
ret[i] = ptrs[f]
}
i++
}
return ret
}
//GetPtrs 获取order_detail对象指针
func (o *OrderDetail) GetPtrs(fields *types.FieldMask, vptr map[int]interface{}) []interface{} {
ptrs := map[string]interface{}{
"order_id": &o.OrderID,
"buyer": &o.Buyer,
"tel": &o.Tel,
"personal_id": &o.PersonalID,
"express_co": &o.ExpressCO,
"express_no": &o.ExpressNO,
"express_type": &o.ExpressType,
"remark": &o.Remark,
"device_type": &o.DeviceType,
"ip": &o.IP,
"coupon": &o.Coupon,
"deliver_detail": &o.DeliverDetail,
"detail": &o.Detail,
"msource": &o.MSource,
"ctime": &o.CTime,
"mtime": &o.MTime,
}
if fields == nil {
fields = &types.FieldMask{Paths: o.GetFields(nil)}
}
ret := make([]interface{}, len(fields.Paths))
i := 0
for _, f := range fields.Paths {
if vptr != nil && (f == "coupon" || f == "deliver_detail" || f == "detail") {
var s string
ret[i] = &s
vptr[i] = ptrs[f]
} else {
ret[i] = ptrs[f]
}
i++
}
return ret
}
//GetPtrs 获取order_sku对象的字段指针
func (o *OrderSKU) GetPtrs(fields *types.FieldMask, vptr map[int]interface{}) []interface{} {
ptrs := map[string]interface{}{
"order_id": &o.OrderID,
"sku_id": &o.SKUID,
"count": &o.Count,
"origin_price": &o.OriginPrice,
"price": &o.Price,
"seat_ids": &o.SeatIDs,
"ticket_type": &o.TicketType,
"discounts": &o.Discounts,
"ctime": &o.CTime,
"mtime": &o.MTime,
}
if fields == nil {
fields = &types.FieldMask{Paths: o.GetFields(nil)}
}
ret := make([]interface{}, len(fields.Paths))
i := 0
for _, f := range fields.Paths {
if vptr != nil && (f == "discounts" || f == "seat_ids") {
var s string
ret[i] = &s
vptr[i] = ptrs[f]
} else {
ret[i] = ptrs[f]
}
i++
}
return ret
}
//GetPtrs 获取order_pay_charge对象的字段指针
func (o *OrderPayCharge) GetPtrs(fields *types.FieldMask, vptr map[int]interface{}) []interface{} {
ptrs := map[string]interface{}{
"order_id": &o.OrderID,
"charge_id": &o.ChargeID,
"channel": &o.Channel,
"paid": &o.Paid,
"refunded": &o.Refunded,
"ctime": &o.CTime,
"mtime": &o.MTime,
}
if fields == nil {
fields = &types.FieldMask{Paths: o.GetFields(nil)}
}
ret := make([]interface{}, len(fields.Paths))
i := 0
for _, f := range fields.Paths {
if vptr != nil && f == "discounts" {
var s string
ret[i] = &s
vptr[i] = ptrs[f]
} else {
ret[i] = ptrs[f]
}
i++
}
return ret
}
//GetVals 获取order_main对象里的值
func (o *OrderMain) GetVals(fields *types.FieldMask, asString bool) []interface{} {
vals := map[string]interface{}{
"order_id": o.OrderID,
"uid": o.UID,
"order_type": o.OrderType,
"item_id": o.ItemID,
"item_info": o.ItemInfo,
"count": o.Count,
"total_money": o.TotalMoney,
"express_fee": o.ExpressFee,
"pay_money": o.PayMoney,
"pay_channel": o.PayChannel,
"pay_time": o.PayTime,
"source": o.Source,
"status": o.Status,
"sub_status": o.SubStatus,
"refund_status": o.RefundStatus,
"is_deleted": o.IsDeleted,
"ctime": o.CTime,
"mtime": o.MTime,
}
if fields == nil {
fields = &types.FieldMask{Paths: o.GetFields(nil)}
}
ret := make([]interface{}, len(fields.Paths))
i := 0
for _, f := range fields.Paths {
if asString && f == "item_info" {
if b, err := json.Marshal(vals[f]); err == nil {
ret[i] = string(b)
}
} else {
ret[i] = vals[f]
}
i++
}
return ret
}
//GetVals 获取order_detail对象字段的值
func (o *OrderDetail) GetVals(fields *types.FieldMask, asString bool) []interface{} {
vals := map[string]interface{}{
"order_id": o.OrderID,
"buyer": o.Buyer,
"tel": o.Tel,
"personal_id": o.PersonalID,
"express_co": o.ExpressCO,
"express_no": o.ExpressNO,
"express_type": o.ExpressType,
"remark": o.Remark,
"device_type": o.DeviceType,
"ip": o.IP,
"coupon": o.Coupon,
"deliver_detail": o.DeliverDetail,
"detail": o.Detail,
"msource": o.MSource,
"ctime": o.CTime,
"mtime": o.MTime,
}
if fields == nil {
fields = &types.FieldMask{Paths: o.GetFields(nil)}
}
ret := make([]interface{}, len(fields.Paths))
i := 0
for _, f := range fields.Paths {
if asString && (f == "coupon" || f == "deliver_detail" || f == "detail") {
if b, err := json.Marshal(vals[f]); err == nil {
ret[i] = string(b)
}
} else {
ret[i] = vals[f]
}
i++
}
return ret
}
//GetVals 获取order_sku字段的值
func (o *OrderSKU) GetVals(fields *types.FieldMask, asString bool) []interface{} {
vals := map[string]interface{}{
"order_id": o.OrderID,
"sku_id": o.SKUID,
"count": o.Count,
"origin_price": o.OriginPrice,
"price": o.Price,
"seat_ids": o.SeatIDs,
"ticket_type": o.TicketType,
"discounts": o.Discounts,
"ctime": o.CTime,
"mtime": o.MTime,
}
if fields == nil {
fields = &types.FieldMask{Paths: o.GetFields(nil)}
}
ret := make([]interface{}, len(fields.Paths))
i := 0
for _, f := range fields.Paths {
if asString && (f == "discounts" || f == "seat_ids") {
if b, err := json.Marshal(vals[f]); err == nil {
ret[i] = string(b)
}
} else {
ret[i] = vals[f]
}
i++
}
return ret
}
func (o *OrderDetail) getEncryptPtrs() []*string {
res := make([]*string, 3)
res[0] = &o.Tel
res[1] = &o.PersonalID
if o.DeliverDetail != nil {
res[2] = &o.DeliverDetail.Tel
}
return res
}
//Encrypt 加密order_detail的字段
func (o *OrderDetail) Encrypt(c *encoding.EncryptConfig) {
for _, p := range o.getEncryptPtrs() {
if p != nil {
s, _ := encoding.Encrypt(*p, c)
*p = s
}
}
}
//Decrypt 解密order_detail字段
func (o *OrderDetail) Decrypt(c *encoding.EncryptConfig) {
for _, p := range o.getEncryptPtrs() {
if p != nil {
s, _ := encoding.Decrypt(*p, c)
*p = s
}
}
}
//GetSettleOrdersRequest 获取结算订单请求
type GetSettleOrdersRequest struct {
Date string `form:"date" validate:"required"`
Ref byte `form:"ref"`
ExtParams string `form:"extParams" validate:"omitempty,numeric"`
PageSize int `form:"pagesize"`
}
//SettleOrder 获取结算订单返回
type SettleOrder struct {
ID int64 `json:"-"`
OrderID int64 `json:"order_id"`
RefID int64 `json:"ref_id"`
RefundApplyTime time.Time `json:"-"`
}
//SettleOrders 获取结算订单返回
type SettleOrders struct {
Data []*SettleOrder `json:"data"`
ExtParams string `json:"extParams"`
}

View File

@@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["account.go"],
importpath = "go-common/app/service/openplatform/ticket-sales/model/order_checker/account",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/vip/model:go_default_library",
"//app/service/openplatform/ticket-sales/api/grpc/v1:go_default_library",
"//app/service/openplatform/ticket-sales/dao:go_default_library",
"//library/ecode: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,68 @@
package account
import (
"context"
"time"
acc "go-common/app/service/main/account/api"
mAcc "go-common/app/service/main/account/model"
vip "go-common/app/service/main/vip/model"
rpc "go-common/app/service/openplatform/ticket-sales/api/grpc/v1"
"go-common/app/service/openplatform/ticket-sales/dao"
"go-common/library/ecode"
)
//Checker 检查用户信息
type Checker struct {
dao *dao.Dao
Users *acc.CardsReply
}
//New 新建一个用户检查类
func New(d *dao.Dao) *Checker {
return &Checker{
dao: d,
}
}
//Check 检查用户条件
func (ac *Checker) Check(ctx context.Context, req *rpc.CreateOrdersRequest) (ee []ecode.Codes, err error) {
l := len(req.Orders)
ee = make([]ecode.Codes, l)
uids := make([]int64, l)
uidMap := make(map[int64]bool, l)
i := 0
for _, v := range req.Orders {
if ok := uidMap[v.UID]; !ok {
uids[i] = v.UID
uidMap[v.UID] = true
i++
}
}
uids = uids[:i]
if ac.Users, err = ac.dao.GetUserCards(ctx, uids); err != nil {
return
}
begin:
for k, v := range req.Orders {
if _, ok := ac.Users.Cards[v.UID]; !ok {
ee[k] = ecode.TicketInvalidUser
continue begin
}
}
return
}
//GetUser 获取用户信息
func (ac *Checker) GetUser(mid int64) *mAcc.Card {
u, ok := ac.Users.Cards[mid]
if !ok {
return nil
}
v := u.Vip
if v.Type != vip.NotVip && v.Status != vip.VipStatusNotOverTime && v.DueDate <= time.Now().Unix() {
return u
}
u.Vip.Type = vip.NotVip
return u
}

View File

@@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["item.go"],
importpath = "go-common/app/service/openplatform/ticket-sales/model/order_checker/item",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account/model:go_default_library",
"//app/service/main/vip/model:go_default_library",
"//app/service/openplatform/ticket-item/api/grpc/v1:go_default_library",
"//app/service/openplatform/ticket-sales/api/grpc/v1:go_default_library",
"//app/service/openplatform/ticket-sales/dao:go_default_library",
"//app/service/openplatform/ticket-sales/model/consts:go_default_library",
"//app/service/openplatform/ticket-sales/model/order_checker/account:go_default_library",
"//library/ecode: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,238 @@
package item
import (
"context"
"strconv"
"strings"
"time"
acc "go-common/app/service/main/account/model"
vip "go-common/app/service/main/vip/model"
itm "go-common/app/service/openplatform/ticket-item/api/grpc/v1"
rpc "go-common/app/service/openplatform/ticket-sales/api/grpc/v1"
"go-common/app/service/openplatform/ticket-sales/dao"
"go-common/app/service/openplatform/ticket-sales/model/consts"
"go-common/app/service/openplatform/ticket-sales/model/order_checker/account"
"go-common/library/ecode"
)
//Checker 检查商品信息
type Checker struct {
dao *dao.Dao
ItemInfos *itm.BillReply
ac *account.Checker
Prices []Prices
}
//Prices 输出订单商品价格
type Prices struct {
Total int64
Pay int64
ExpFee int64
}
//New 新建一个检查类
func New(d *dao.Dao, ac *account.Checker) *Checker {
return &Checker{
dao: d,
ac: ac,
}
}
//Check 检查商品信息
func (ic *Checker) Check(ctx context.Context, req *rpc.CreateOrdersRequest) (ee []ecode.Codes, err error) {
itemIDs, scIDs, tkIDs := getItemIds(req.Orders)
ic.ItemInfos, err = ic.dao.ItemBillInfo(ctx, itemIDs, scIDs, tkIDs)
if err != nil {
return
}
ee = make([]ecode.Codes, len(req.Orders))
ic.Prices = make([]Prices, len(req.Orders))
begin:
for k, v := range req.Orders {
//检查订单类型
if _, ok := consts.OrderTypes[v.OrderType]; !ok {
ee[k] = ecode.TicketParamInvalid
continue begin
}
//检查项目
if e := ic.checkItem(v); e != nil {
ee[k] = e
continue begin
}
//检查购买人信息
if e := ic.checkBuyer(v); e != nil {
ee[k] = e
continue begin
}
user := ic.ac.GetUser(v.UID)
//检查票种
cnt, skuIDs, e := ic.checkSKU(v, user)
if e != nil {
ee[k] = e
continue begin
}
//检查剩余可购数
boughtCnt, _ := ic.dao.RawBoughtCount(ctx, strconv.FormatInt(v.UID, 10), v.ProjectID, skuIDs)
if e = ic.buyLimit(v, user, boughtCnt, cnt); e != nil {
ee[k] = e
continue begin
}
//检查商品价格
ic.Prices[k], e = ic.checkPrice(v, user)
if e != nil {
ee[k] = e
continue begin
}
}
return
}
//getItemIds 依次返回订单请求中的商品ID、场次ID、票价ID
func getItemIds(orders []*rpc.CreateOrderRequest) ([]int64, []int64, []int64) {
l := len(orders)
itemIDsMp, scIDsMp, tkIDsMp := make(map[int64]bool, l), make(map[int64]bool, l), make(map[int64]bool, l)
itemIDs, scIDs, tkIDs := make([]int64, l), make([]int64, l), make([]int64, l)
var i1, i2, i3 int
for _, o := range orders {
if _, ok := itemIDsMp[o.ProjectID]; !ok {
itemIDsMp[o.ProjectID] = true
itemIDs[i1] = o.ProjectID
i1++
}
if _, ok := scIDsMp[o.ScreenID]; !ok {
scIDsMp[o.ScreenID] = true
scIDs[i2] = o.ScreenID
i2++
}
for _, sku := range o.SKUs {
if _, ok := tkIDsMp[sku.SKUID]; !ok {
tkIDsMp[sku.SKUID] = true
tkIDs[i3] = sku.SKUID
i3++
}
}
}
return itemIDs[:i1], scIDs[:i2], tkIDs[:i3]
}
func (ic *Checker) checkItem(o *rpc.CreateOrderRequest) ecode.Codes {
//检查项目是否存在
if _, ok := ic.ItemInfos.BaseInfo[o.ProjectID]; !ok {
return ecode.TicketRecordLost
}
//todo 检查项目状态
if _, ok := ic.ItemInfos.BillOpt[o.ProjectID]; !ok {
ic.ItemInfos.BillOpt[o.ProjectID] = &itm.BillOpt{}
}
//检查场次是否存在
scr, ok := ic.ItemInfos.BaseInfo[o.ProjectID].Screen[o.ScreenID]
if !ok {
return ecode.TicketRecordLost
}
//不选座场次必传sku
if scr.PickSeat == consts.PickSeatNo && len(o.SKUs) == 0 {
return ecode.TicketMissData
}
//检查必填配送信息
if scr.DeliveryType == consts.DeliverTypeExpress && (o.DeliverDetail.Tel == "" || o.DeliverDetail.Addr == "" || o.DeliverDetail.Name == "") {
return ecode.TicketMissData
}
return nil
}
func (ic *Checker) checkBuyer(o *rpc.CreateOrderRequest) ecode.Codes {
sBuyer := ic.ItemInfos.BillOpt[o.ProjectID].BuyerInfo
if sBuyer != "" && len(o.Buyers) == 0 {
return ecode.TicketMissData
}
aBuyer := strings.Split(sBuyer, ",")
for _, v := range aBuyer {
f, _ := strconv.Atoi(v)
if (f == consts.BuyerInfoTel && o.Buyers[0].Tel == "") ||
(f == consts.BuyerInfoPerID && o.Buyers[0].PersonalID == "") {
return ecode.TicketMissData
}
}
return nil
}
func (ic *Checker) checkSKU(o *rpc.CreateOrderRequest, user *acc.Card) (cnt int64, skuIDs []int64, e ecode.Codes) {
skuIDs = make([]int64, len(o.SKUs))
now := time.Now().Unix()
//检查每个sku可售态
for k, sku := range o.SKUs {
skuIDs[k] = sku.SKUID
tk, ok := ic.ItemInfos.BaseInfo[o.ProjectID].Screen[o.ScreenID].Ticket[skuIDs[k]]
if sku.Count <= 0 || !ok {
e = ecode.TicketParamInvalid
return
}
if tk.Time.SaleStime > now {
e = ecode.TicketSaleNotStart
return
}
if tk.Time.SaleEtime < now {
e = ecode.TicketSaleEnd
return
}
if user.Vip.Type < tk.BuyLimit {
e = ecode.TicketNoPriv
return
}
cnt += sku.Count
}
return
}
//buyLimit 检查订单可购买数,返回单用户可购买数与单笔订单可购买数
func (ic *Checker) buyLimit(o *rpc.CreateOrderRequest, user *acc.Card, boughtCnt int64, buyCnt int64) ecode.Codes {
var lvLimits []*itm.BnlLevel
var vipLimits map[int32]*itm.BnlLevel
var oLimit, uLimit int64
if opt, ok := ic.ItemInfos.BillOpt[o.ProjectID]; ok && opt.BuyLimit != nil {
lvLimits = opt.BuyLimit.Level
vipLimits = opt.BuyLimit.VIP
oLimit = int64(opt.BuyLimit.Per)
}
uLevel, vipTyp := user.Level, user.Vip.Type
if int(uLevel) >= len(lvLimits) {
uLevel = 0
}
if uLimit == 0 {
oLimit = consts.DefaultBuyNumLimit
}
if vipTyp == vip.NotVip || lvLimits[uLevel].ApplyToVip == 1 {
uLimit = int64(lvLimits[uLevel].Max)
} else {
uLimit = int64(vipLimits[vipTyp].Max)
}
if buyCnt > oLimit {
return ecode.TicketExceedLimit
}
if uLimit-boughtCnt-buyCnt <= 0 {
return ecode.TicketExceedLimit
}
return nil
}
func (ic *Checker) checkPrice(o *rpc.CreateOrderRequest, user *acc.Card) (p Prices, e ecode.Codes) {
opt := ic.ItemInfos.BillOpt[o.ProjectID]
//非大会员不免邮
if user == nil || user.Vip.Type != vip.AnnualVip || opt.VipExpFree == 0 {
p.ExpFee = int64(opt.ExpTip)
if p.ExpFee < 0 {
p.ExpFee = 0
}
}
p.Total = p.ExpFee
for _, s := range o.SKUs {
tk := ic.ItemInfos.BaseInfo[o.ProjectID].Screen[o.ScreenID].Ticket[int64(s.SKUID)]
p.Total += int64(tk.PriceList.Price) * s.Count
}
p.Pay = p.Total
if p.Pay != o.PayMoney {
e = ecode.TicketPriceChanged
}
return
}

View File

@@ -0,0 +1,96 @@
package model
import (
"bytes"
"crypto/md5"
"fmt"
"sort"
"strconv"
"go-common/library/log"
xtime "go-common/library/time"
)
// MsgContent 支付回调 msgContent 字段的结构
// 由于 MsgContent 可能增/减字段故使用 map 兼容
type MsgContent map[string]string
// Charge 支付回调结构体
type Charge struct {
ID string
Paid bool
Refunded bool
OrderID int64
DeviceType int64
Channel int64
Amount int64
TimePaid xtime.Time
}
// ValidSign 签名校验
func (m MsgContent) ValidSign() (ok bool) {
sign := m["sign"]
delete(m, "sign")
var keys []string
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)
buf := bytes.Buffer{}
for _, k := range keys {
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(k)
buf.WriteString("=")
buf.WriteString(m[k])
}
fmt.Println(buf.String())
h := md5.New()
ok = string(h.Sum(buf.Bytes())) == sign
return
}
// ToCharge 生成 charge 结构
func (m MsgContent) ToCharge() (charge *Charge, err error) {
charge = &Charge{}
charge.ID = m["txId"]
charge.Paid = true
charge.Refunded = false
orderID, err := strconv.ParseInt(m["orderId"], 10, 64)
if err != nil {
log.Error("MsgContent.ToCharge() strconv.ParseInt(orderId: %s) error(%v)", m["orderId"], err)
return
}
charge.OrderID = orderID
deviceType, err := strconv.ParseInt(m["deviceType"], 10, 64)
if err != nil {
log.Error("MsgContent.ToCharge() strconv.ParseInt(deviceType: %s) error(%v)", m["deviceType"], err)
return
}
charge.DeviceType = deviceType
payChannel, err := strconv.ParseInt(m["payChannel"], 10, 64)
if err != nil {
log.Error("MsgContent.ToCharge() strconv.ParseInt(payChannel: %s) error(%v)", m["payChannel"], err)
return
}
charge.Channel = payChannel
amount, err := strconv.ParseInt(m["amount"], 10, 64)
if err != nil {
log.Error("MsgContent.ToCharge() strconv.ParseInt(amount: %s) error(%v)", m["amount"], err)
return
}
charge.Amount = amount
if err = charge.TimePaid.Scan(m["orderPayTime"]); err != nil {
log.Error("MsgContent.ToCharge() TimePaid.Scan(%v) error(%v)", m["orderPayTime"], err)
return
}
return
}

View File

@@ -0,0 +1,48 @@
package model
import "go-common/library/time"
//Promotion 活动表
type Promotion struct {
PromoID int64 `json:"promo_id"`
Type int16 `json:"type"`
ItemID int64 `json:"item_id"`
SKUID int64 `json:"sku_id"`
Extra int64 `json:"extra"`
ExpireSec int64 `json:"expire_sec"`
SKUCount int64 `json:"sku_count"`
Amount int64 `json:"amount"`
BuyerCount int64 `json:"buyer_count"`
BeginTime int64 `json:"begin_time"`
EndTime int64 `json:"end_time"`
Status int16 `json:"status"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
PrivSKUID int64 `json:"priv_sku_id"`
UsableCoupons string `json:"usable_coupons"`
}
//PromotionGroup 拼团表
type PromotionGroup struct {
PromoID int64 `json:"promo_id"`
GroupID int64 `json:"group_id"`
UID int64 `json:"uid"`
OrderCount int64 `json:"order_count"`
Status int16 `json:"status"`
ExpireAt int64 `json:"expire_at"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}
//PromotionOrder 拼团订单表
type PromotionOrder struct {
PromoID int64 `json:"promo_id"`
GroupID int64 `json:"group_id"`
OrderID int64 `json:"order_id"`
IsMaster int16 `json:"is_master"`
UID int64 `json:"uid"`
Status int16 `json:"status"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
SKUID int64 `json:"sku_id"`
}

View File

@@ -0,0 +1,29 @@
package model
import "go-common/library/time"
// SKUStockLog sku 库存操作日志
type SKUStockLog struct {
ID int64 `db:"id"`
SKUID int64 `db:"sku_id"`
OpType int16 `db:"op_type"`
SrcID int64 `db:"src_id"`
Stock int64 `db:"stock"`
CanceledAt time.Time `db:"canceled_at"`
Ctime time.Time `db:"ctime,autogen"`
Mtime time.Time `db:"mtime,autogen"`
}
// SKUStock sku 库存
type SKUStock struct {
SKUID int64 `db:"sku_id"`
ParentSKUID int64 `db:"parent_sku_id"`
ItemID int64 `db:"item_id"`
Specs string `db:"specs"`
TotalStock int64 `db:"total_stock"`
Stock int64 `db:"stock"`
LockedStock int64 `db:"locked_stock"`
SkAlert int64 `db:"sk_alert"`
Ctime time.Time `db:"ctime,autogen"`
Mtime time.Time `db:"mtime,autogen"`
}

View File

@@ -0,0 +1,41 @@
package model
import (
xtime "go-common/library/time"
)
// Ticket ticket 表结构
type Ticket struct {
ID int64 `json:"id"`
UID int64 `json:"uid"`
OID int64 `json:"oid"`
SID int64 `json:"sid"`
Price int64 `json:"price"`
Src int16 `json:"src"`
Type int16 `json:"type"`
Status int16 `json:"status"`
Qr string `json:"qr"`
RefID int64 `json:"ref_id"`
SkuID int64 `json:"sku_id"`
SeatID int64 `json:"seat_id"`
Seat string `json:"seat"`
RefundApplyTime xtime.Time `json:"refund_apply_time"`
ETime xtime.Time `json:"etime"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
}
// TicketSend ticket_send 表结构
type TicketSend struct {
ID int64 `json:"id"`
SID int64 `son:"sid"`
SendTID int64 `json:"send_tid"`
RecvTID int64 `json:"recv_tid"`
SendUID int64 `json:"send_uid"`
RecvUID int64 `json:"recv_uid"`
RecvTel string `json:"recv_tel"`
Status int16 `json:"status"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
OID int64 `json:"oid"`
}