2 Commits

12 changed files with 61 additions and 18 deletions

View File

@@ -762,6 +762,7 @@ create table product_sku (
price_min decimal not null,
status int not null default 1,
sort int not null default 0,
count_min int not null default 1,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
@@ -780,6 +781,7 @@ comment on column product_sku.name is 'SKU 可读名称';
comment on column product_sku.price_min is '最低价格';
comment on column product_sku.status is 'SKU状态0-禁用1-正常';
comment on column product_sku.sort is '排序';
comment on column product_sku.count_min is '最小购买数量';
comment on column product_sku.created_at is '创建时间';
comment on column product_sku.updated_at is '更新时间';
comment on column product_sku.deleted_at is '删除时间';
@@ -816,6 +818,7 @@ create table resource (
code text,
type int not null,
active bool not null default true,
checkip bool not null default true,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
@@ -830,9 +833,10 @@ comment on table resource is '套餐表';
comment on column resource.id is '套餐ID';
comment on column resource.user_id is '用户ID';
comment on column resource.resource_no is '套餐编号';
comment on column resource.active is '套餐状态';
comment on column resource.type is '套餐类型1-短效动态2-长效动态';
comment on column resource.code is '产品编码';
comment on column resource.type is '套餐类型1-短效动态2-长效动态';
comment on column resource.active is '套餐状态';
comment on column resource.checkip is '提取时是否检查 ip 地址';
comment on column resource.created_at is '创建时间';
comment on column resource.updated_at is '更新时间';
comment on column resource.deleted_at is '删除时间';

View File

@@ -182,6 +182,9 @@ func TradeCreate(c *fiber.Ctx) error {
if err != nil {
return err
}
if authCtx.User.IDType == m.UserIDTypeUnverified {
return core.NewBizErr("请先实名认证后再购买")
}
// 解析请求参数
req := new(TradeCreateReq)

View File

@@ -17,6 +17,7 @@ type ProductSku struct {
PriceMin decimal.Decimal `json:"price_min" gorm:"column:price_min"` // 最低价格
Status SkuStatus `json:"status" gorm:"column:status"` // SKU 状态0-禁用1-正常
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
CountMin int32 `json:"count_min" gorm:"column:count_min"` // 最小购买数量
Product *Product `json:"product,omitempty" gorm:"foreignKey:ProductID"`
Discount *ProductDiscount `json:"discount,omitempty" gorm:"foreignKey:DiscountId"`

View File

@@ -12,6 +12,7 @@ type Resource struct {
Active bool `json:"active" gorm:"column:active"` // 套餐状态
Type ResourceType `json:"type" gorm:"column:type"` // 套餐类型1-短效动态2-长效动态
Code string `json:"code" gorm:"column:code"` // 产品编码
CheckIP bool `json:"checkip" gorm:"column:checkip"` // 是否检查IP
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Short *ResourceShort `json:"short,omitempty" gorm:"foreignKey:ResourceID"`

View File

@@ -39,6 +39,7 @@ func newProductSku(db *gorm.DB, opts ...gen.DOOption) productSku {
_productSku.PriceMin = field.NewField(tableName, "price_min")
_productSku.Status = field.NewInt32(tableName, "status")
_productSku.Sort = field.NewInt32(tableName, "sort")
_productSku.CountMin = field.NewInt32(tableName, "count_min")
_productSku.Product = productSkuBelongsToProduct{
db: db.Session(&gorm.Session{}),
@@ -93,6 +94,7 @@ type productSku struct {
PriceMin field.Field
Status field.Int32
Sort field.Int32
CountMin field.Int32
Product productSkuBelongsToProduct
Discount productSkuBelongsToDiscount
@@ -124,6 +126,7 @@ func (p *productSku) updateTableName(table string) *productSku {
p.PriceMin = field.NewField(table, "price_min")
p.Status = field.NewInt32(table, "status")
p.Sort = field.NewInt32(table, "sort")
p.CountMin = field.NewInt32(table, "count_min")
p.fillFieldMap()
@@ -140,7 +143,7 @@ func (p *productSku) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (p *productSku) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 14)
p.fieldMap = make(map[string]field.Expr, 15)
p.fieldMap["id"] = p.ID
p.fieldMap["created_at"] = p.CreatedAt
p.fieldMap["updated_at"] = p.UpdatedAt
@@ -153,6 +156,7 @@ func (p *productSku) fillFieldMap() {
p.fieldMap["price_min"] = p.PriceMin
p.fieldMap["status"] = p.Status
p.fieldMap["sort"] = p.Sort
p.fieldMap["count_min"] = p.CountMin
}

View File

@@ -36,6 +36,7 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
_resource.Active = field.NewBool(tableName, "active")
_resource.Type = field.NewInt(tableName, "type")
_resource.Code = field.NewString(tableName, "code")
_resource.CheckIP = field.NewBool(tableName, "checkip")
_resource.Short = resourceHasOneShort{
db: db.Session(&gorm.Session{}),
@@ -185,6 +186,7 @@ type resource struct {
Active field.Bool
Type field.Int
Code field.String
CheckIP field.Bool
Short resourceHasOneShort
Long resourceHasOneLong
@@ -217,6 +219,7 @@ func (r *resource) updateTableName(table string) *resource {
r.Active = field.NewBool(table, "active")
r.Type = field.NewInt(table, "type")
r.Code = field.NewString(table, "code")
r.CheckIP = field.NewBool(table, "checkip")
r.fillFieldMap()
@@ -233,7 +236,7 @@ func (r *resource) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (r *resource) fillFieldMap() {
r.fieldMap = make(map[string]field.Expr, 13)
r.fieldMap = make(map[string]field.Expr, 14)
r.fieldMap["id"] = r.ID
r.fieldMap["created_at"] = r.CreatedAt
r.fieldMap["updated_at"] = r.UpdatedAt
@@ -243,6 +246,7 @@ func (r *resource) fillFieldMap() {
r.fieldMap["active"] = r.Active
r.fieldMap["type"] = r.Type
r.fieldMap["code"] = r.Code
r.fieldMap["checkip"] = r.CheckIP
}

View File

@@ -83,10 +83,11 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
return nil, ErrResourceNotExist
}
var info = &ResourceView{
Id: resource.ID,
User: *resource.User,
Active: resource.Active,
Type: resource.Type,
Id: resource.ID,
User: *resource.User,
Active: resource.Active,
Type: resource.Type,
CheckIP: resource.CheckIP,
}
switch resource.Type {
@@ -142,6 +143,7 @@ type ResourceView struct {
Daily int32
LastAt *time.Time
Today int // 今日用量
CheckIP bool
}
// 检查用户是否可提取
@@ -178,7 +180,7 @@ func ensure(now time.Time, source netip.Addr, resourceId int32, count int) (*Res
pass = true
}
}
if !pass {
if resource.CheckIP && !pass {
return nil, nil, core.NewBizErr(fmt.Sprintf("IP 地址 %s 不在白名单内", source.String()))
}

View File

@@ -50,6 +50,7 @@ func (s *productService) AllProductSaleInfos() ([]*m.Product, error) {
q.ProductSku.Name,
q.ProductSku.Code,
q.ProductSku.Price,
q.ProductSku.CountMin,
).
Where(
q.ProductSku.ProductID.In(pids...),

View File

@@ -47,6 +47,11 @@ func (s *productSkuService) Create(create CreateProductSkuData) (err error) {
return core.NewBizErr("产品最低价格的格式不正确", err)
}
countMin := int32(1)
if create.CountMin != nil {
countMin = *create.CountMin
}
return q.ProductSku.Create(&m.ProductSku{
ProductID: create.ProductID,
DiscountId: create.DiscountID,
@@ -54,6 +59,8 @@ func (s *productSkuService) Create(create CreateProductSkuData) (err error) {
Name: create.Name,
Price: price,
PriceMin: priceMin,
Sort: create.Sort,
CountMin: countMin,
})
}
@@ -64,6 +71,8 @@ type CreateProductSkuData struct {
Name string `json:"name"`
Price string `json:"price"`
PriceMin string `json:"price_min"`
Sort int32 `json:"sort"`
CountMin *int32 `json:"count_min"`
}
func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
@@ -95,6 +104,12 @@ func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
if update.Status != nil {
do = append(do, q.ProductSku.Status.Value(*update.Status))
}
if update.Sort != nil {
do = append(do, q.ProductSku.Sort.Value(*update.Sort))
}
if update.CountMin != nil {
do = append(do, q.ProductSku.CountMin.Value(*update.CountMin))
}
_, err = q.ProductSku.Where(q.ProductSku.ID.Eq(update.ID)).UpdateSimple(do...)
return err
@@ -108,6 +123,8 @@ type UpdateProductSkuData struct {
Price *string `json:"price"`
PriceMin string `json:"price_min"`
Status *int32 `json:"status"`
Sort *int32 `json:"sort"`
CountMin *int32 `json:"count_min"`
}
func (s *productSkuService) Delete(id int32) (err error) {

View File

@@ -188,7 +188,7 @@ func (s *proxyService) Remove(id int32) error {
type UpdateProxyStatus struct {
ID int32 `json:"id" validate:"required"`
Status m.ProxyStatus `json:"status" validate:"required"`
Status m.ProxyStatus `json:"status"`
}
func (s *proxyService) UpdateStatus(update *UpdateProxyStatus) error {

View File

@@ -121,14 +121,13 @@ func (s *resourceService) Create(q *q.Query, uid int32, now time.Time, data *Cre
}
func (s *resourceService) Update(data *UpdateResourceData) error {
if data.Active == nil {
return core.NewBizErr("更新套餐失败active 不能为空")
}
do := make([]field.AssignExpr, 0)
if data.Active != nil {
do = append(do, q.Resource.Active.Value(*data.Active))
}
if data.CheckIP != nil {
do = append(do, q.Resource.CheckIP.Value(*data.CheckIP))
}
_, err := q.Resource.
Where(q.Resource.ID.Eq(data.Id)).
@@ -142,10 +141,14 @@ func (s *resourceService) Update(data *UpdateResourceData) error {
type UpdateResourceData struct {
core.IdReq
Active *bool `json:"active"`
Active *bool `json:"active"`
CheckIP *bool `json:"checkip"`
}
func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, cuid *int32) (*m.ProductSku, *m.ProductDiscount, *m.CouponUser, decimal.Decimal, decimal.Decimal, decimal.Decimal, error) {
if count <= 0 {
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewBizErr("购买数量必须大于 0")
}
sku, err := q.ProductSku.
Joins(q.ProductSku.Discount).
@@ -153,7 +156,7 @@ func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, c
Take()
if err != nil {
slog.Debug("查询产品失败", "skuCode", skuCode)
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewBizErr(fmt.Sprintf("产品不可用", skuCode), err)
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewBizErr("产品不可用", err)
}
// 原价
@@ -163,7 +166,7 @@ func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, c
// 折扣价
discount := sku.Discount
if discount == nil {
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewServErr("价格查询失败", err)
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewServErr("产品未配置折扣", err)
}
discountRate := discount.Rate()
@@ -201,7 +204,7 @@ func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, c
couponApplied = amountMin.Copy()
}
return sku, discount, coupon, amount, discounted, couponApplied, nil
return sku, discount, coupon, amount.RoundCeil(2), discounted.RoundCeil(2), couponApplied.RoundCeil(2), nil
}
type CreateResourceData struct {

View File

@@ -88,6 +88,9 @@ type UpdateBalanceData struct {
}
func (data *UpdateBalanceData) TradeDetail(user *m.User) (*TradeDetail, error) {
if data.Amount <= 0 {
return nil, core.NewBizErr("充值金额必须大于0")
}
amount := decimal.NewFromInt(int64(data.Amount)).Div(decimal.NewFromInt(100))
return &TradeDetail{
data,