重构优化套餐数据结构,修复提取计数问题
This commit is contained in:
@@ -713,14 +713,13 @@ drop table if exists resource_short cascade;
|
|||||||
create table resource_short (
|
create table resource_short (
|
||||||
id int generated by default as identity primary key,
|
id int generated by default as identity primary key,
|
||||||
resource_id int not null,
|
resource_id int not null,
|
||||||
type int not null,
|
|
||||||
live int not null,
|
live int not null,
|
||||||
expire timestamptz,
|
type int not null,
|
||||||
quota int,
|
quota int not null,
|
||||||
|
expire_at timestamptz,
|
||||||
used int not null default 0,
|
used int not null default 0,
|
||||||
daily_limit int not null default 0,
|
daily int not null default 0,
|
||||||
daily_used int not null default 0,
|
last_at timestamptz
|
||||||
daily_last timestamptz
|
|
||||||
);
|
);
|
||||||
create index idx_resource_short_resource_id on resource_short (resource_id);
|
create index idx_resource_short_resource_id on resource_short (resource_id);
|
||||||
|
|
||||||
@@ -728,28 +727,26 @@ create index idx_resource_short_resource_id on resource_short (resource_id);
|
|||||||
comment on table resource_short is '短效动态套餐表';
|
comment on table resource_short is '短效动态套餐表';
|
||||||
comment on column resource_short.id is 'ID';
|
comment on column resource_short.id is 'ID';
|
||||||
comment on column resource_short.resource_id is '套餐ID';
|
comment on column resource_short.resource_id is '套餐ID';
|
||||||
comment on column resource_short.type is '套餐类型:1-包时,2-包量';
|
|
||||||
comment on column resource_short.live is '可用时长(秒)';
|
comment on column resource_short.live is '可用时长(秒)';
|
||||||
comment on column resource_short.quota is '配额数量';
|
comment on column resource_short.type is '套餐类型:1-包时,2-包量';
|
||||||
comment on column resource_short.used is '已用数量';
|
comment on column resource_short.quota is '每日配额(包时)或总配额(包量)';
|
||||||
comment on column resource_short.expire is '过期时间';
|
comment on column resource_short.expire_at is '套餐过期时间,包时模式可用';
|
||||||
comment on column resource_short.daily_limit is '每日限制';
|
comment on column resource_short.used is '总用量';
|
||||||
comment on column resource_short.daily_used is '今日已用数量';
|
comment on column resource_short.daily is '当日用量';
|
||||||
comment on column resource_short.daily_last is '今日最后使用时间';
|
comment on column resource_short.last_at is '最后使用时间';
|
||||||
|
|
||||||
-- resource_long
|
-- resource_long
|
||||||
drop table if exists resource_long cascade;
|
drop table if exists resource_long cascade;
|
||||||
create table resource_long (
|
create table resource_long (
|
||||||
id int generated by default as identity primary key,
|
id int generated by default as identity primary key,
|
||||||
resource_id int not null,
|
resource_id int not null,
|
||||||
type int not null,
|
|
||||||
live int not null,
|
live int not null,
|
||||||
expire timestamptz,
|
type int not null,
|
||||||
quota int,
|
quota int not null,
|
||||||
|
expire_at timestamptz,
|
||||||
used int not null default 0,
|
used int not null default 0,
|
||||||
daily_limit int not null default 0,
|
daily int not null default 0,
|
||||||
daily_used int not null default 0,
|
last_at timestamptz
|
||||||
daily_last timestamptz
|
|
||||||
);
|
);
|
||||||
create index idx_resource_long_resource_id on resource_long (resource_id);
|
create index idx_resource_long_resource_id on resource_long (resource_id);
|
||||||
|
|
||||||
@@ -757,14 +754,13 @@ create index idx_resource_long_resource_id on resource_long (resource_id);
|
|||||||
comment on table resource_long is '长效动态套餐表';
|
comment on table resource_long is '长效动态套餐表';
|
||||||
comment on column resource_long.id is 'ID';
|
comment on column resource_long.id is 'ID';
|
||||||
comment on column resource_long.resource_id is '套餐ID';
|
comment on column resource_long.resource_id is '套餐ID';
|
||||||
|
comment on column resource_long.live is '可用时长(小时)';
|
||||||
comment on column resource_long.type is '套餐类型:1-包时,2-包量';
|
comment on column resource_long.type is '套餐类型:1-包时,2-包量';
|
||||||
comment on column resource_long.live is '可用时长(天)';
|
comment on column resource_long.quota is '每日配额(包时)或总配额(包量)';
|
||||||
comment on column resource_long.quota is '配额数量';
|
comment on column resource_long.expire_at is '套餐过期时间,包时模式可用';
|
||||||
comment on column resource_long.used is '已用数量';
|
comment on column resource_long.used is '总用量';
|
||||||
comment on column resource_long.expire is '过期时间';
|
comment on column resource_long.daily is '当日用量';
|
||||||
comment on column resource_long.daily_limit is '每日限制';
|
comment on column resource_long.last_at is '最后使用时间';
|
||||||
comment on column resource_long.daily_used is '今日已用数量';
|
|
||||||
comment on column resource_long.daily_last is '今日最后使用时间';
|
|
||||||
|
|
||||||
-- endregion
|
-- endregion
|
||||||
|
|
||||||
|
|||||||
@@ -60,10 +60,10 @@ func ListResourceShort(c *fiber.Ctx) error {
|
|||||||
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
|
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
|
||||||
}
|
}
|
||||||
if req.ExpireAfter != nil {
|
if req.ExpireAfter != nil {
|
||||||
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Gte(*req.ExpireAfter))
|
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Gte(*req.ExpireAfter))
|
||||||
}
|
}
|
||||||
if req.ExpireBefore != nil {
|
if req.ExpireBefore != nil {
|
||||||
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Lte(*req.ExpireBefore))
|
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Lte(*req.ExpireBefore))
|
||||||
}
|
}
|
||||||
|
|
||||||
resource, err := q.Resource.Where(do).
|
resource, err := q.Resource.Where(do).
|
||||||
@@ -141,10 +141,10 @@ func ListResourceLong(c *fiber.Ctx) error {
|
|||||||
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
|
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
|
||||||
}
|
}
|
||||||
if req.ExpireAfter != nil {
|
if req.ExpireAfter != nil {
|
||||||
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Gte(*req.ExpireAfter))
|
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Gte(*req.ExpireAfter))
|
||||||
}
|
}
|
||||||
if req.ExpireBefore != nil {
|
if req.ExpireBefore != nil {
|
||||||
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Lte(*req.ExpireBefore))
|
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Lte(*req.ExpireBefore))
|
||||||
}
|
}
|
||||||
|
|
||||||
resource, err := q.Resource.Where(do).
|
resource, err := q.Resource.Where(do).
|
||||||
@@ -204,10 +204,10 @@ func AllActiveResource(c *fiber.Ctx) error {
|
|||||||
q.Resource.Type.Eq(int(m.ResourceTypeShort)),
|
q.Resource.Type.Eq(int(m.ResourceTypeShort)),
|
||||||
q.ResourceShort.As(q.Resource.Short.Name()).Where(
|
q.ResourceShort.As(q.Resource.Short.Name()).Where(
|
||||||
short.Type.Eq(int(m.ResourceModeTime)),
|
short.Type.Eq(int(m.ResourceModeTime)),
|
||||||
short.Expire.Gte(now),
|
short.ExpireAt.Gte(now),
|
||||||
q.ResourceShort.As(q.Resource.Short.Name()).
|
q.ResourceShort.As(q.Resource.Short.Name()).
|
||||||
Where(short.DailyLast.Lt(u.Today())).
|
Where(short.LastAt.Lt(u.Today())).
|
||||||
Or(short.DailyLimit.GtCol(short.DailyUsed)),
|
Or(short.Quota.GtCol(short.Daily)),
|
||||||
).Or(
|
).Or(
|
||||||
short.Type.Eq(int(m.ResourceModeQuota)),
|
short.Type.Eq(int(m.ResourceModeQuota)),
|
||||||
short.Quota.GtCol(short.Used),
|
short.Quota.GtCol(short.Used),
|
||||||
@@ -216,10 +216,10 @@ func AllActiveResource(c *fiber.Ctx) error {
|
|||||||
q.Resource.Type.Eq(int(m.ResourceTypeLong)),
|
q.Resource.Type.Eq(int(m.ResourceTypeLong)),
|
||||||
q.ResourceLong.As(q.Resource.Long.Name()).Where(
|
q.ResourceLong.As(q.Resource.Long.Name()).Where(
|
||||||
long.Type.Eq(int(m.ResourceModeTime)),
|
long.Type.Eq(int(m.ResourceModeTime)),
|
||||||
long.Expire.Gte(now),
|
long.ExpireAt.Gte(now),
|
||||||
q.ResourceLong.As(q.Resource.Long.Name()).
|
q.ResourceLong.As(q.Resource.Long.Name()).
|
||||||
Where(long.DailyLast.Lt(u.Today())).
|
Where(long.LastAt.Lt(u.Today())).
|
||||||
Or(long.DailyLimit.GtCol(long.DailyUsed)),
|
Or(long.Quota.GtCol(long.Daily)),
|
||||||
).Or(
|
).Or(
|
||||||
long.Type.Eq(int(m.ResourceModeQuota)),
|
long.Type.Eq(int(m.ResourceModeQuota)),
|
||||||
long.Quota.GtCol(long.Used),
|
long.Quota.GtCol(long.Used),
|
||||||
@@ -282,39 +282,39 @@ func StatisticResourceFree(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// 短效包量
|
// 短效包量
|
||||||
case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeQuota:
|
case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeQuota:
|
||||||
if u.Z(resource.Short.Quota) > resource.Short.Used {
|
if resource.Short.Quota > resource.Short.Used {
|
||||||
shortCount++
|
shortCount++
|
||||||
shortQuotaSum += int(u.Z(resource.Short.Quota) - resource.Short.Used)
|
shortQuotaSum += int(resource.Short.Quota - resource.Short.Used)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 长效包量
|
// 长效包量
|
||||||
case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeQuota:
|
case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeQuota:
|
||||||
if u.Z(resource.Long.Quota) > resource.Long.Used {
|
if resource.Long.Quota > resource.Long.Used {
|
||||||
longCount++
|
longCount++
|
||||||
longQuotaSum += int(u.Z(resource.Long.Quota) - resource.Long.Used)
|
longQuotaSum += int(resource.Long.Quota - resource.Long.Used)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 短效包时
|
// 短效包时
|
||||||
case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeTime:
|
case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeTime:
|
||||||
if time.Time(*resource.Short.Expire).After(time.Now()) {
|
if time.Time(*resource.Short.ExpireAt).After(time.Now()) {
|
||||||
if resource.Short.DailyLast == nil || u.IsToday(time.Time(*resource.Short.DailyLast)) == false {
|
if resource.Short.LastAt == nil || u.IsToday(time.Time(*resource.Short.LastAt)) == false {
|
||||||
shortCount++
|
shortCount++
|
||||||
shortDailyFreeSum += int(resource.Short.DailyLimit)
|
shortDailyFreeSum += int(resource.Short.Quota)
|
||||||
} else if resource.Short.DailyLimit > resource.Short.DailyUsed {
|
} else if resource.Short.Quota > resource.Short.Daily {
|
||||||
shortCount++
|
shortCount++
|
||||||
shortDailyFreeSum += int(resource.Short.DailyLimit - resource.Short.DailyUsed)
|
shortDailyFreeSum += int(resource.Short.Quota - resource.Short.Daily)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 长效包时
|
// 长效包时
|
||||||
case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeTime:
|
case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeTime:
|
||||||
if time.Time(*resource.Long.Expire).After(time.Now()) {
|
if time.Time(*resource.Long.ExpireAt).After(time.Now()) {
|
||||||
if resource.Long.DailyLast == nil || u.IsToday(time.Time(*resource.Long.DailyLast)) == false {
|
if resource.Long.LastAt == nil || u.IsToday(time.Time(*resource.Long.LastAt)) == false {
|
||||||
longCount++
|
longCount++
|
||||||
longDailyFreeSum += int(resource.Long.DailyLimit)
|
longDailyFreeSum += int(resource.Long.Quota)
|
||||||
} else if resource.Long.DailyLimit > resource.Long.DailyUsed {
|
} else if resource.Long.Quota > resource.Long.Daily {
|
||||||
longCount++
|
longCount++
|
||||||
longDailyFreeSum += int(resource.Long.DailyLimit - resource.Long.DailyUsed)
|
longDailyFreeSum += int(resource.Long.Quota - resource.Long.Daily)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,7 +443,11 @@ func ResourcePrice(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取套餐价格
|
// 获取套餐价格
|
||||||
|
amount, err := req.GetAmount()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"price": req.GetAmount().StringFixed(2),
|
"price": amount.StringFixed(2),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,11 @@ import (
|
|||||||
type ResourceLong struct {
|
type ResourceLong struct {
|
||||||
ID int32 `json:"id" gorm:"column:id"` // ID
|
ID int32 `json:"id" gorm:"column:id"` // ID
|
||||||
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
|
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
|
||||||
|
Live int32 `json:"live" gorm:"column:live"` // 可用时长(小时)
|
||||||
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型:1-包时,2-包量
|
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型:1-包时,2-包量
|
||||||
Live int32 `json:"live" gorm:"column:live"` // 可用时长(天)
|
Quota int32 `json:"quota" gorm:"column:quota"` // 每日配额(包时)或总配额(包量)
|
||||||
Expire *time.Time `json:"expire" gorm:"column:expire"` // 过期时间
|
ExpireAt *time.Time `json:"expire_at" gorm:"column:expire_at"` // 套餐过期时间,包时模式可用
|
||||||
Quota *int32 `json:"quota" gorm:"column:quota"` // 配额数量
|
Used int32 `json:"used" gorm:"column:used"` // 总用量
|
||||||
Used int32 `json:"used" gorm:"column:used"` // 已用数量
|
Daily int32 `json:"daily" gorm:"column:daily"` // 当日用量
|
||||||
DailyLimit int32 `json:"daily_limit" gorm:"column:daily_limit"` // 每日限制
|
LastAt *time.Time `json:"last_at" gorm:"column:last_at"` // 最后使用时间
|
||||||
DailyUsed int32 `json:"daily_used" gorm:"column:daily_used"` // 今日已用数量
|
|
||||||
DailyLast *time.Time `json:"daily_last" gorm:"column:daily_last"` // 今日最后使用时间
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,11 @@ import (
|
|||||||
type ResourceShort struct {
|
type ResourceShort struct {
|
||||||
ID int32 `json:"id" gorm:"column:id"` // ID
|
ID int32 `json:"id" gorm:"column:id"` // ID
|
||||||
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
|
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
|
||||||
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型:1-包时,2-包量
|
|
||||||
Live int32 `json:"live" gorm:"column:live"` // 可用时长(秒)
|
Live int32 `json:"live" gorm:"column:live"` // 可用时长(秒)
|
||||||
Expire *time.Time `json:"expire" gorm:"column:expire"` // 过期时间
|
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型:1-包时,2-包量
|
||||||
Quota *int32 `json:"quota" gorm:"column:quota"` // 配额数量
|
Quota int32 `json:"quota" gorm:"column:quota"` // 每日配额(包时)或总配额(包量)
|
||||||
Used int32 `json:"used" gorm:"column:used"` // 已用数量
|
ExpireAt *time.Time `json:"expire_at" gorm:"column:expire_at"` // 套餐过期时间,包时模式可用
|
||||||
DailyLimit int32 `json:"daily_limit" gorm:"column:daily_limit"` // 每日限制
|
Used int32 `json:"used" gorm:"column:used"` // 总用量
|
||||||
DailyUsed int32 `json:"daily_used" gorm:"column:daily_used"` // 今日已用数量
|
Daily int32 `json:"daily" gorm:"column:daily"` // 当日用量
|
||||||
DailyLast *time.Time `json:"daily_last" gorm:"column:daily_last"` // 今日最后使用时间
|
LastAt *time.Time `json:"last_at" gorm:"column:last_at"` // 最后使用时间
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,14 +29,13 @@ func newResourceLong(db *gorm.DB, opts ...gen.DOOption) resourceLong {
|
|||||||
_resourceLong.ALL = field.NewAsterisk(tableName)
|
_resourceLong.ALL = field.NewAsterisk(tableName)
|
||||||
_resourceLong.ID = field.NewInt32(tableName, "id")
|
_resourceLong.ID = field.NewInt32(tableName, "id")
|
||||||
_resourceLong.ResourceID = field.NewInt32(tableName, "resource_id")
|
_resourceLong.ResourceID = field.NewInt32(tableName, "resource_id")
|
||||||
_resourceLong.Type = field.NewInt(tableName, "type")
|
|
||||||
_resourceLong.Live = field.NewInt32(tableName, "live")
|
_resourceLong.Live = field.NewInt32(tableName, "live")
|
||||||
_resourceLong.Expire = field.NewTime(tableName, "expire")
|
_resourceLong.Type = field.NewInt(tableName, "type")
|
||||||
_resourceLong.Quota = field.NewInt32(tableName, "quota")
|
_resourceLong.Quota = field.NewInt32(tableName, "quota")
|
||||||
|
_resourceLong.ExpireAt = field.NewTime(tableName, "expire_at")
|
||||||
_resourceLong.Used = field.NewInt32(tableName, "used")
|
_resourceLong.Used = field.NewInt32(tableName, "used")
|
||||||
_resourceLong.DailyLimit = field.NewInt32(tableName, "daily_limit")
|
_resourceLong.Daily = field.NewInt32(tableName, "daily")
|
||||||
_resourceLong.DailyUsed = field.NewInt32(tableName, "daily_used")
|
_resourceLong.LastAt = field.NewTime(tableName, "last_at")
|
||||||
_resourceLong.DailyLast = field.NewTime(tableName, "daily_last")
|
|
||||||
|
|
||||||
_resourceLong.fillFieldMap()
|
_resourceLong.fillFieldMap()
|
||||||
|
|
||||||
@@ -49,14 +48,13 @@ type resourceLong struct {
|
|||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int32
|
ID field.Int32
|
||||||
ResourceID field.Int32
|
ResourceID field.Int32
|
||||||
Type field.Int
|
|
||||||
Live field.Int32
|
Live field.Int32
|
||||||
Expire field.Time
|
Type field.Int
|
||||||
Quota field.Int32
|
Quota field.Int32
|
||||||
|
ExpireAt field.Time
|
||||||
Used field.Int32
|
Used field.Int32
|
||||||
DailyLimit field.Int32
|
Daily field.Int32
|
||||||
DailyUsed field.Int32
|
LastAt field.Time
|
||||||
DailyLast field.Time
|
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
}
|
}
|
||||||
@@ -75,14 +73,13 @@ func (r *resourceLong) updateTableName(table string) *resourceLong {
|
|||||||
r.ALL = field.NewAsterisk(table)
|
r.ALL = field.NewAsterisk(table)
|
||||||
r.ID = field.NewInt32(table, "id")
|
r.ID = field.NewInt32(table, "id")
|
||||||
r.ResourceID = field.NewInt32(table, "resource_id")
|
r.ResourceID = field.NewInt32(table, "resource_id")
|
||||||
r.Type = field.NewInt(table, "type")
|
|
||||||
r.Live = field.NewInt32(table, "live")
|
r.Live = field.NewInt32(table, "live")
|
||||||
r.Expire = field.NewTime(table, "expire")
|
r.Type = field.NewInt(table, "type")
|
||||||
r.Quota = field.NewInt32(table, "quota")
|
r.Quota = field.NewInt32(table, "quota")
|
||||||
|
r.ExpireAt = field.NewTime(table, "expire_at")
|
||||||
r.Used = field.NewInt32(table, "used")
|
r.Used = field.NewInt32(table, "used")
|
||||||
r.DailyLimit = field.NewInt32(table, "daily_limit")
|
r.Daily = field.NewInt32(table, "daily")
|
||||||
r.DailyUsed = field.NewInt32(table, "daily_used")
|
r.LastAt = field.NewTime(table, "last_at")
|
||||||
r.DailyLast = field.NewTime(table, "daily_last")
|
|
||||||
|
|
||||||
r.fillFieldMap()
|
r.fillFieldMap()
|
||||||
|
|
||||||
@@ -99,17 +96,16 @@ func (r *resourceLong) GetFieldByName(fieldName string) (field.OrderExpr, bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceLong) fillFieldMap() {
|
func (r *resourceLong) fillFieldMap() {
|
||||||
r.fieldMap = make(map[string]field.Expr, 10)
|
r.fieldMap = make(map[string]field.Expr, 9)
|
||||||
r.fieldMap["id"] = r.ID
|
r.fieldMap["id"] = r.ID
|
||||||
r.fieldMap["resource_id"] = r.ResourceID
|
r.fieldMap["resource_id"] = r.ResourceID
|
||||||
r.fieldMap["type"] = r.Type
|
|
||||||
r.fieldMap["live"] = r.Live
|
r.fieldMap["live"] = r.Live
|
||||||
r.fieldMap["expire"] = r.Expire
|
r.fieldMap["type"] = r.Type
|
||||||
r.fieldMap["quota"] = r.Quota
|
r.fieldMap["quota"] = r.Quota
|
||||||
|
r.fieldMap["expire_at"] = r.ExpireAt
|
||||||
r.fieldMap["used"] = r.Used
|
r.fieldMap["used"] = r.Used
|
||||||
r.fieldMap["daily_limit"] = r.DailyLimit
|
r.fieldMap["daily"] = r.Daily
|
||||||
r.fieldMap["daily_used"] = r.DailyUsed
|
r.fieldMap["last_at"] = r.LastAt
|
||||||
r.fieldMap["daily_last"] = r.DailyLast
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r resourceLong) clone(db *gorm.DB) resourceLong {
|
func (r resourceLong) clone(db *gorm.DB) resourceLong {
|
||||||
|
|||||||
@@ -29,14 +29,13 @@ func newResourceShort(db *gorm.DB, opts ...gen.DOOption) resourceShort {
|
|||||||
_resourceShort.ALL = field.NewAsterisk(tableName)
|
_resourceShort.ALL = field.NewAsterisk(tableName)
|
||||||
_resourceShort.ID = field.NewInt32(tableName, "id")
|
_resourceShort.ID = field.NewInt32(tableName, "id")
|
||||||
_resourceShort.ResourceID = field.NewInt32(tableName, "resource_id")
|
_resourceShort.ResourceID = field.NewInt32(tableName, "resource_id")
|
||||||
_resourceShort.Type = field.NewInt(tableName, "type")
|
|
||||||
_resourceShort.Live = field.NewInt32(tableName, "live")
|
_resourceShort.Live = field.NewInt32(tableName, "live")
|
||||||
_resourceShort.Expire = field.NewTime(tableName, "expire")
|
_resourceShort.Type = field.NewInt(tableName, "type")
|
||||||
_resourceShort.Quota = field.NewInt32(tableName, "quota")
|
_resourceShort.Quota = field.NewInt32(tableName, "quota")
|
||||||
|
_resourceShort.ExpireAt = field.NewTime(tableName, "expire_at")
|
||||||
_resourceShort.Used = field.NewInt32(tableName, "used")
|
_resourceShort.Used = field.NewInt32(tableName, "used")
|
||||||
_resourceShort.DailyLimit = field.NewInt32(tableName, "daily_limit")
|
_resourceShort.Daily = field.NewInt32(tableName, "daily")
|
||||||
_resourceShort.DailyUsed = field.NewInt32(tableName, "daily_used")
|
_resourceShort.LastAt = field.NewTime(tableName, "last_at")
|
||||||
_resourceShort.DailyLast = field.NewTime(tableName, "daily_last")
|
|
||||||
|
|
||||||
_resourceShort.fillFieldMap()
|
_resourceShort.fillFieldMap()
|
||||||
|
|
||||||
@@ -49,14 +48,13 @@ type resourceShort struct {
|
|||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int32
|
ID field.Int32
|
||||||
ResourceID field.Int32
|
ResourceID field.Int32
|
||||||
Type field.Int
|
|
||||||
Live field.Int32
|
Live field.Int32
|
||||||
Expire field.Time
|
Type field.Int
|
||||||
Quota field.Int32
|
Quota field.Int32
|
||||||
|
ExpireAt field.Time
|
||||||
Used field.Int32
|
Used field.Int32
|
||||||
DailyLimit field.Int32
|
Daily field.Int32
|
||||||
DailyUsed field.Int32
|
LastAt field.Time
|
||||||
DailyLast field.Time
|
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
}
|
}
|
||||||
@@ -75,14 +73,13 @@ func (r *resourceShort) updateTableName(table string) *resourceShort {
|
|||||||
r.ALL = field.NewAsterisk(table)
|
r.ALL = field.NewAsterisk(table)
|
||||||
r.ID = field.NewInt32(table, "id")
|
r.ID = field.NewInt32(table, "id")
|
||||||
r.ResourceID = field.NewInt32(table, "resource_id")
|
r.ResourceID = field.NewInt32(table, "resource_id")
|
||||||
r.Type = field.NewInt(table, "type")
|
|
||||||
r.Live = field.NewInt32(table, "live")
|
r.Live = field.NewInt32(table, "live")
|
||||||
r.Expire = field.NewTime(table, "expire")
|
r.Type = field.NewInt(table, "type")
|
||||||
r.Quota = field.NewInt32(table, "quota")
|
r.Quota = field.NewInt32(table, "quota")
|
||||||
|
r.ExpireAt = field.NewTime(table, "expire_at")
|
||||||
r.Used = field.NewInt32(table, "used")
|
r.Used = field.NewInt32(table, "used")
|
||||||
r.DailyLimit = field.NewInt32(table, "daily_limit")
|
r.Daily = field.NewInt32(table, "daily")
|
||||||
r.DailyUsed = field.NewInt32(table, "daily_used")
|
r.LastAt = field.NewTime(table, "last_at")
|
||||||
r.DailyLast = field.NewTime(table, "daily_last")
|
|
||||||
|
|
||||||
r.fillFieldMap()
|
r.fillFieldMap()
|
||||||
|
|
||||||
@@ -99,17 +96,16 @@ func (r *resourceShort) GetFieldByName(fieldName string) (field.OrderExpr, bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceShort) fillFieldMap() {
|
func (r *resourceShort) fillFieldMap() {
|
||||||
r.fieldMap = make(map[string]field.Expr, 10)
|
r.fieldMap = make(map[string]field.Expr, 9)
|
||||||
r.fieldMap["id"] = r.ID
|
r.fieldMap["id"] = r.ID
|
||||||
r.fieldMap["resource_id"] = r.ResourceID
|
r.fieldMap["resource_id"] = r.ResourceID
|
||||||
r.fieldMap["type"] = r.Type
|
|
||||||
r.fieldMap["live"] = r.Live
|
r.fieldMap["live"] = r.Live
|
||||||
r.fieldMap["expire"] = r.Expire
|
r.fieldMap["type"] = r.Type
|
||||||
r.fieldMap["quota"] = r.Quota
|
r.fieldMap["quota"] = r.Quota
|
||||||
|
r.fieldMap["expire_at"] = r.ExpireAt
|
||||||
r.fieldMap["used"] = r.Used
|
r.fieldMap["used"] = r.Used
|
||||||
r.fieldMap["daily_limit"] = r.DailyLimit
|
r.fieldMap["daily"] = r.Daily
|
||||||
r.fieldMap["daily_used"] = r.DailyUsed
|
r.fieldMap["last_at"] = r.LastAt
|
||||||
r.fieldMap["daily_last"] = r.DailyLast
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r resourceShort) clone(db *gorm.DB) resourceShort {
|
func (r resourceShort) clone(db *gorm.DB) resourceShort {
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"platform/pkg/u"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
g "platform/web/globals"
|
g "platform/web/globals"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
@@ -66,7 +68,7 @@ func genPassPair() (string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查找资源
|
// 查找资源
|
||||||
func findResource(resourceId int32) (*ResourceView, error) {
|
func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
||||||
resource, err := q.Resource.
|
resource, err := q.Resource.
|
||||||
Preload(field.Associations).
|
Preload(field.Associations).
|
||||||
Where(
|
Where(
|
||||||
@@ -82,57 +84,43 @@ func findResource(resourceId int32) (*ResourceView, error) {
|
|||||||
}
|
}
|
||||||
var info = &ResourceView{
|
var info = &ResourceView{
|
||||||
Id: resource.ID,
|
Id: resource.ID,
|
||||||
|
User: *resource.User,
|
||||||
Active: resource.Active,
|
Active: resource.Active,
|
||||||
Type: resource.Type,
|
Type: resource.Type,
|
||||||
User: *resource.User,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch resource.Type {
|
switch resource.Type {
|
||||||
case m.ResourceTypeShort:
|
case m.ResourceTypeShort:
|
||||||
var sub = resource.Short
|
var sub = resource.Short
|
||||||
var dailyLast = time.Time{}
|
info.ShortId = &sub.ID
|
||||||
if sub.DailyLast != nil {
|
info.ExpireAt = sub.ExpireAt
|
||||||
dailyLast = time.Time(*sub.DailyLast)
|
|
||||||
}
|
|
||||||
var expire = time.Time{}
|
|
||||||
if sub.Expire != nil {
|
|
||||||
expire = time.Time(*sub.Expire)
|
|
||||||
}
|
|
||||||
var quota int32
|
|
||||||
if sub.Quota != nil {
|
|
||||||
quota = *sub.Quota
|
|
||||||
}
|
|
||||||
info.Mode = sub.Type
|
|
||||||
info.Live = time.Duration(sub.Live) * time.Second
|
info.Live = time.Duration(sub.Live) * time.Second
|
||||||
info.DailyLimit = sub.DailyLimit
|
info.Mode = sub.Type
|
||||||
info.DailyUsed = sub.DailyUsed
|
info.Quota = sub.Quota
|
||||||
info.DailyLast = dailyLast
|
|
||||||
info.Expire = expire
|
|
||||||
info.Quota = quota
|
|
||||||
info.Used = sub.Used
|
info.Used = sub.Used
|
||||||
|
info.Daily = sub.Daily
|
||||||
|
info.LastAt = sub.LastAt
|
||||||
|
if sub.LastAt != nil && u.IsSameDate(*sub.LastAt, now) {
|
||||||
|
info.Today = int(sub.Daily)
|
||||||
|
}
|
||||||
|
|
||||||
case m.ResourceTypeLong:
|
case m.ResourceTypeLong:
|
||||||
var sub = resource.Long
|
var sub = resource.Long
|
||||||
var dailyLast = time.Time{}
|
info.LongId = &sub.ID
|
||||||
if sub.DailyLast != nil {
|
info.ExpireAt = sub.ExpireAt
|
||||||
dailyLast = time.Time(*sub.DailyLast)
|
info.Live = time.Duration(sub.Live) * time.Hour
|
||||||
}
|
|
||||||
var expire = time.Time{}
|
|
||||||
if sub.Expire != nil {
|
|
||||||
expire = time.Time(*sub.Expire)
|
|
||||||
}
|
|
||||||
var quota int32
|
|
||||||
if sub.Quota != nil {
|
|
||||||
quota = *sub.Quota
|
|
||||||
}
|
|
||||||
info.Mode = sub.Type
|
info.Mode = sub.Type
|
||||||
info.Live = time.Duration(sub.Live) * time.Hour * 24
|
info.Quota = sub.Quota
|
||||||
info.DailyLimit = sub.DailyLimit
|
|
||||||
info.DailyUsed = sub.DailyUsed
|
|
||||||
info.DailyLast = dailyLast
|
|
||||||
info.Expire = expire
|
|
||||||
info.Quota = quota
|
|
||||||
info.Used = sub.Used
|
info.Used = sub.Used
|
||||||
|
info.Daily = sub.Daily
|
||||||
|
info.LastAt = sub.LastAt
|
||||||
|
if sub.LastAt != nil && u.IsSameDate(*sub.LastAt, now) {
|
||||||
|
info.Today = int(sub.Daily)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Mode == m.ResourceModeTime && info.ExpireAt == nil {
|
||||||
|
return nil, errors.New("检查套餐获取时间失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
return info, nil
|
return info, nil
|
||||||
@@ -141,17 +129,19 @@ func findResource(resourceId int32) (*ResourceView, error) {
|
|||||||
// ResourceView 套餐数据的简化视图,便于直接获取主要数据
|
// ResourceView 套餐数据的简化视图,便于直接获取主要数据
|
||||||
type ResourceView struct {
|
type ResourceView struct {
|
||||||
Id int32
|
Id int32
|
||||||
|
User m.User
|
||||||
Active bool
|
Active bool
|
||||||
Type m.ResourceType
|
Type m.ResourceType
|
||||||
Mode m.ResourceMode
|
ShortId *int32
|
||||||
|
LongId *int32
|
||||||
Live time.Duration
|
Live time.Duration
|
||||||
DailyLimit int32
|
Mode m.ResourceMode
|
||||||
DailyUsed int32
|
|
||||||
DailyLast time.Time
|
|
||||||
Quota int32
|
Quota int32
|
||||||
|
ExpireAt *time.Time
|
||||||
Used int32
|
Used int32
|
||||||
Expire time.Time
|
Daily int32
|
||||||
User m.User
|
LastAt *time.Time
|
||||||
|
Today int // 今日用量
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查用户是否可提取
|
// 检查用户是否可提取
|
||||||
@@ -161,7 +151,7 @@ func ensure(now time.Time, source netip.Addr, resourceId int32, count int) (*Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户套餐
|
// 获取用户套餐
|
||||||
resource, err := findResource(resourceId)
|
resource, err := findResource(resourceId, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -200,16 +190,11 @@ func ensure(now time.Time, source netip.Addr, resourceId int32, count int) (*Res
|
|||||||
// 包时
|
// 包时
|
||||||
case m.ResourceModeTime:
|
case m.ResourceModeTime:
|
||||||
// 检查过期时间
|
// 检查过期时间
|
||||||
if resource.Expire.Before(now) {
|
if resource.ExpireAt.Before(now) {
|
||||||
return nil, nil, ErrResourceExpired
|
return nil, nil, ErrResourceExpired
|
||||||
}
|
}
|
||||||
// 检查每日限额
|
// 检查每日限额
|
||||||
used := 0
|
if count+resource.Today > int(resource.Quota) {
|
||||||
if now.Format("2006-01-02") == resource.DailyLast.Format("2006-01-02") {
|
|
||||||
used = int(resource.DailyUsed)
|
|
||||||
}
|
|
||||||
excess := used+count > int(resource.DailyLimit)
|
|
||||||
if excess {
|
|
||||||
return nil, nil, ErrResourceDailyLimit
|
return nil, nil, ErrResourceDailyLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hibiken/asynq"
|
"github.com/hibiken/asynq"
|
||||||
|
"gorm.io/gen"
|
||||||
"gorm.io/gen/field"
|
"gorm.io/gen/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -152,44 +153,48 @@ func (s *channelBaiyinService) CreateChannels(source netip.Addr, resourceId int3
|
|||||||
|
|
||||||
// 保存数据
|
// 保存数据
|
||||||
err = q.Q.Transaction(func(q *q.Query) error {
|
err = q.Q.Transaction(func(q *q.Query) error {
|
||||||
|
var rs gen.ResultInfo
|
||||||
|
|
||||||
// 更新套餐用量
|
// 根据套餐类型和模式更新使用记录
|
||||||
used := int32(count)
|
isShortType := resource.Type == m.ResourceTypeShort
|
||||||
if u.IsSameDate(now, resource.DailyLast) {
|
isLongType := resource.Type == m.ResourceTypeLong
|
||||||
used += resource.DailyUsed
|
|
||||||
}
|
|
||||||
|
|
||||||
switch resource.Type {
|
switch {
|
||||||
case m.ResourceTypeShort:
|
case isShortType:
|
||||||
_, err = q.ResourceShort.
|
rs, err = q.ResourceShort.Debug().
|
||||||
Where(
|
Where(
|
||||||
q.ResourceShort.ResourceID.Eq(resource.Id),
|
q.ResourceShort.ID.Eq(*resource.ShortId),
|
||||||
q.ResourceShort.Used.Eq(resource.Used),
|
q.ResourceShort.Used.Eq(resource.Used),
|
||||||
q.ResourceShort.DailyUsed.Eq(resource.DailyUsed),
|
q.ResourceShort.Daily.Eq(resource.Daily),
|
||||||
q.ResourceShort.DailyLast.Eq(resource.DailyLast),
|
|
||||||
).
|
).
|
||||||
UpdateSimple(
|
UpdateSimple(
|
||||||
q.ResourceShort.Used.Add(int32(count)),
|
q.ResourceShort.Used.Add(int32(count)),
|
||||||
q.ResourceShort.DailyUsed.Value(used),
|
q.ResourceShort.Daily.Value(int32(resource.Today+count)),
|
||||||
q.ResourceShort.DailyLast.Value(now),
|
q.ResourceShort.LastAt.Value(now),
|
||||||
)
|
)
|
||||||
case m.ResourceTypeLong:
|
|
||||||
_, err = q.ResourceLong.
|
case isLongType:
|
||||||
|
rs, err = q.ResourceLong.Debug().
|
||||||
Where(
|
Where(
|
||||||
q.ResourceLong.ResourceID.Eq(resource.Id),
|
q.ResourceLong.ID.Eq(*resource.LongId),
|
||||||
q.ResourceLong.Used.Eq(resource.Used),
|
q.ResourceLong.Used.Eq(resource.Used),
|
||||||
q.ResourceLong.DailyUsed.Eq(resource.DailyUsed),
|
q.ResourceLong.Daily.Eq(resource.Daily),
|
||||||
q.ResourceLong.DailyLast.Eq(resource.DailyLast),
|
|
||||||
).
|
).
|
||||||
UpdateSimple(
|
UpdateSimple(
|
||||||
q.ResourceLong.Used.Add(int32(count)),
|
q.ResourceLong.Used.Add(int32(count)),
|
||||||
q.ResourceLong.DailyUsed.Value(used),
|
q.ResourceLong.Daily.Value(int32(resource.Today+count)),
|
||||||
q.ResourceLong.DailyLast.Value(now),
|
q.ResourceLong.LastAt.Value(now),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return core.NewServErr("套餐类型不正确,无法更新", nil)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新套餐使用记录失败", err)
|
return core.NewServErr("更新套餐使用记录失败", err)
|
||||||
}
|
}
|
||||||
|
if rs.RowsAffected == 0 {
|
||||||
|
return core.NewServErr("套餐使用记录不存在")
|
||||||
|
}
|
||||||
|
|
||||||
// 保存通道
|
// 保存通道
|
||||||
err = q.Channel.
|
err = q.Channel.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"platform/pkg/u"
|
"platform/pkg/u"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
@@ -29,15 +30,19 @@ func (s *resourceService) CreateResourceByBalance(uid int32, now time.Time, data
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查余额
|
// 检查余额
|
||||||
var amount = user.Balance.Sub(data.GetAmount())
|
amount, err := data.GetAmount()
|
||||||
if amount.IsNegative() {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
balance := user.Balance.Sub(amount)
|
||||||
|
if balance.IsNegative() {
|
||||||
return ErrBalanceNotEnough
|
return ErrBalanceNotEnough
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户余额
|
// 更新用户余额
|
||||||
_, err = q.User.
|
_, err = q.User.
|
||||||
Where(q.User.ID.Eq(uid), q.User.Balance.Eq(user.Balance)).
|
Where(q.User.ID.Eq(uid), q.User.Balance.Eq(user.Balance)).
|
||||||
UpdateSimple(q.User.Balance.Value(amount))
|
UpdateSimple(q.User.Balance.Value(balance))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新用户余额失败", err)
|
return core.NewServErr("更新用户余额失败", err)
|
||||||
}
|
}
|
||||||
@@ -49,7 +54,11 @@ func (s *resourceService) CreateResourceByBalance(uid int32, now time.Time, data
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成账单
|
// 生成账单
|
||||||
err = q.Bill.Create(newForConsume(uid, Bill.GenNo(), data.GetSubject(), data.GetAmount(), resource))
|
subject, err := data.GetSubject()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = q.Bill.Create(newForConsume(uid, Bill.GenNo(), subject, amount, resource))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("生成账单失败", err)
|
return core.NewServErr("生成账单失败", err)
|
||||||
}
|
}
|
||||||
@@ -61,6 +70,13 @@ func (s *resourceService) CreateResourceByBalance(uid int32, now time.Time, data
|
|||||||
|
|
||||||
func (s *resourceService) CreateResourceByTrade(uid int32, now time.Time, data *CreateResourceData, trade *m.Trade) error {
|
func (s *resourceService) CreateResourceByTrade(uid int32, now time.Time, data *CreateResourceData, trade *m.Trade) error {
|
||||||
return q.Q.Transaction(func(q *q.Query) error {
|
return q.Q.Transaction(func(q *q.Query) error {
|
||||||
|
// 检查交易
|
||||||
|
if trade == nil {
|
||||||
|
return core.NewBizErr("交易数据不能为空")
|
||||||
|
}
|
||||||
|
if trade.Status != m.TradeStatusSuccess {
|
||||||
|
return core.NewBizErr("交易状态不正确")
|
||||||
|
}
|
||||||
|
|
||||||
// 保存套餐
|
// 保存套餐
|
||||||
resource, err := createResource(q, uid, now, data)
|
resource, err := createResource(q, uid, now, data)
|
||||||
@@ -69,7 +85,15 @@ func (s *resourceService) CreateResourceByTrade(uid int32, now time.Time, data *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成账单
|
// 生成账单
|
||||||
err = q.Bill.Create(newForConsume(uid, Bill.GenNo(), data.GetSubject(), data.GetAmount(), resource, trade))
|
subject, err := data.GetSubject()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
amount, err := data.GetAmount()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = q.Bill.Create(newForConsume(uid, Bill.GenNo(), subject, amount, resource, trade))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("生成账单失败", err)
|
return core.NewServErr("生成账单失败", err)
|
||||||
}
|
}
|
||||||
@@ -95,13 +119,17 @@ func createResource(q *q.Query, uid int32, now time.Time, data *CreateResourceDa
|
|||||||
if short == nil {
|
if short == nil {
|
||||||
return nil, core.NewBizErr("短效套餐数据不能为空")
|
return nil, core.NewBizErr("短效套餐数据不能为空")
|
||||||
}
|
}
|
||||||
var duration = time.Duration(short.Expire) * 24 * time.Hour
|
|
||||||
resource.Short = &m.ResourceShort{
|
resource.Short = &m.ResourceShort{
|
||||||
Type: short.Mode,
|
|
||||||
Live: short.Live,
|
Live: short.Live,
|
||||||
Quota: &short.Quota,
|
Type: short.Mode,
|
||||||
Expire: u.P(now.Add(duration)),
|
Quota: short.Quota,
|
||||||
DailyLimit: short.DailyLimit,
|
}
|
||||||
|
if short.Mode == m.ResourceModeTime {
|
||||||
|
if short.Expire == nil {
|
||||||
|
return nil, core.NewBizErr("包时套餐过期时间不能为空")
|
||||||
|
}
|
||||||
|
var duration = time.Duration(*short.Expire) * 24 * time.Hour
|
||||||
|
resource.Short.ExpireAt = u.P(now.Add(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 长效套餐
|
// 长效套餐
|
||||||
@@ -110,13 +138,17 @@ func createResource(q *q.Query, uid int32, now time.Time, data *CreateResourceDa
|
|||||||
if long == nil {
|
if long == nil {
|
||||||
return nil, core.NewBizErr("长效套餐数据不能为空")
|
return nil, core.NewBizErr("长效套餐数据不能为空")
|
||||||
}
|
}
|
||||||
var duration = time.Duration(long.Expire) * 24 * time.Hour
|
|
||||||
resource.Long = &m.ResourceLong{
|
resource.Long = &m.ResourceLong{
|
||||||
Type: long.Mode,
|
|
||||||
Live: long.Live,
|
Live: long.Live,
|
||||||
Quota: &long.Quota,
|
Type: long.Mode,
|
||||||
Expire: u.P(now.Add(duration)),
|
Quota: long.Quota,
|
||||||
DailyLimit: long.DailyLimit,
|
}
|
||||||
|
if long.Mode == m.ResourceModeTime {
|
||||||
|
if long.Expire == nil {
|
||||||
|
return nil, core.NewBizErr("包时套餐过期时间不能为空")
|
||||||
|
}
|
||||||
|
var duration = time.Duration(*long.Expire) * 24 * time.Hour
|
||||||
|
resource.Long.ExpireAt = u.P(now.Add(duration))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return nil, core.NewBizErr("不支持的套餐类型")
|
return nil, core.NewBizErr("不支持的套餐类型")
|
||||||
@@ -139,20 +171,18 @@ type CreateResourceData struct {
|
|||||||
type CreateShortResourceData struct {
|
type CreateShortResourceData struct {
|
||||||
Live int32 `json:"live" validate:"required,min=180"`
|
Live int32 `json:"live" validate:"required,min=180"`
|
||||||
Mode m.ResourceMode `json:"mode" validate:"required"`
|
Mode m.ResourceMode `json:"mode" validate:"required"`
|
||||||
Expire int32 `json:"expire"`
|
Quota int32 `json:"quota"`
|
||||||
DailyLimit int32 `json:"daily_limit" validate:"min=2000"`
|
Expire *int32 `json:"expire"`
|
||||||
Quota int32 `json:"quota" validate:"min=10000"`
|
|
||||||
|
|
||||||
name string
|
name string
|
||||||
price *decimal.Decimal
|
price *decimal.Decimal
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateLongResourceData struct {
|
type CreateLongResourceData struct {
|
||||||
Live int32 `json:"live" validate:"required,oneof=1 4 8 12 24"`
|
Live int32 `json:"live" validate:"required"`
|
||||||
Mode m.ResourceMode `json:"mode" validate:"required,oneof=1 2"`
|
Mode m.ResourceMode `json:"mode" validate:"required"`
|
||||||
Expire int32 `json:"expire"`
|
Quota int32 `json:"quota" validate:"required"`
|
||||||
DailyLimit int32 `json:"daily_limit" validate:"min=100"`
|
Expire *int32 `json:"expire" validate:"required"`
|
||||||
Quota int32 `json:"quota" validate:"min=500"`
|
|
||||||
|
|
||||||
name string
|
name string
|
||||||
price *decimal.Decimal
|
price *decimal.Decimal
|
||||||
@@ -162,24 +192,28 @@ func (c *CreateResourceData) GetType() m.TradeType {
|
|||||||
return m.TradeTypePurchase
|
return m.TradeTypePurchase
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CreateResourceData) GetSubject() string {
|
func (c *CreateResourceData) GetSubject() (string, error) {
|
||||||
switch c.Type {
|
switch c.Type {
|
||||||
|
default:
|
||||||
|
return "", errors.New("无效的套餐类型")
|
||||||
|
|
||||||
case m.ResourceTypeShort:
|
case m.ResourceTypeShort:
|
||||||
return c.Short.GetSubject()
|
return c.Short.GetSubject()
|
||||||
case m.ResourceTypeLong:
|
case m.ResourceTypeLong:
|
||||||
return c.Long.GetSubject()
|
return c.Long.GetSubject()
|
||||||
}
|
}
|
||||||
panic("类型对应的数据为空")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CreateResourceData) GetAmount() decimal.Decimal {
|
func (c *CreateResourceData) GetAmount() (decimal.Decimal, error) {
|
||||||
switch c.Type {
|
switch c.Type {
|
||||||
|
default:
|
||||||
|
return decimal.Zero, errors.New("无效的套餐类型")
|
||||||
|
|
||||||
case m.ResourceTypeShort:
|
case m.ResourceTypeShort:
|
||||||
return c.Short.GetAmount()
|
return c.Short.GetAmount()
|
||||||
case m.ResourceTypeLong:
|
case m.ResourceTypeLong:
|
||||||
return c.Long.GetAmount()
|
return c.Long.GetAmount()
|
||||||
}
|
}
|
||||||
panic("类型对应的数据为空")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CreateResourceData) Serialize() (string, error) {
|
func (c *CreateResourceData) Serialize() (string, error) {
|
||||||
@@ -191,27 +225,37 @@ func (c *CreateResourceData) Deserialize(str string) error {
|
|||||||
return json.Unmarshal([]byte(str), c)
|
return json.Unmarshal([]byte(str), c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *CreateShortResourceData) GetSubject() string {
|
func (data *CreateShortResourceData) GetSubject() (string, error) {
|
||||||
if data.name == "" {
|
if data.name == "" {
|
||||||
var mode string
|
var mode string
|
||||||
switch data.Mode {
|
switch data.Mode {
|
||||||
case 1:
|
default:
|
||||||
|
return "", errors.New("无效的套餐模式")
|
||||||
|
|
||||||
|
case m.ResourceModeTime:
|
||||||
mode = "包时"
|
mode = "包时"
|
||||||
case 2:
|
case m.ResourceModeQuota:
|
||||||
mode = "包量"
|
mode = "包量"
|
||||||
}
|
}
|
||||||
|
|
||||||
data.name = fmt.Sprintf("短效动态%s %v 分钟", mode, data.Live/60)
|
data.name = fmt.Sprintf("短效动态%s %v 分钟", mode, data.Live/60)
|
||||||
}
|
}
|
||||||
return data.name
|
return data.name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *CreateShortResourceData) GetAmount() decimal.Decimal {
|
func (data *CreateShortResourceData) GetAmount() (decimal.Decimal, error) {
|
||||||
if data.price == nil {
|
if data.price == nil {
|
||||||
var factor int32
|
var factor int32
|
||||||
switch data.Mode {
|
switch data.Mode {
|
||||||
case 1:
|
default:
|
||||||
factor = data.DailyLimit * data.Expire
|
return decimal.Zero, errors.New("无效的套餐模式")
|
||||||
case 2:
|
|
||||||
|
case m.ResourceModeTime:
|
||||||
|
if data.Expire == nil {
|
||||||
|
return decimal.Zero, errors.New("包时套餐过期时间不能为空")
|
||||||
|
}
|
||||||
|
factor = data.Quota * *data.Expire
|
||||||
|
case m.ResourceModeQuota:
|
||||||
factor = data.Quota
|
factor = data.Quota
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,38 +267,53 @@ func (data *CreateShortResourceData) GetAmount() decimal.Decimal {
|
|||||||
var dec = decimal.Decimal{}.
|
var dec = decimal.Decimal{}.
|
||||||
Add(decimal.NewFromInt32(base * factor)).
|
Add(decimal.NewFromInt32(base * factor)).
|
||||||
Div(decimal.NewFromInt(30000))
|
Div(decimal.NewFromInt(30000))
|
||||||
data.price = &dec
|
if dec.IsZero() {
|
||||||
}
|
return decimal.Zero, errors.New("计算金额错误")
|
||||||
return *data.price
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *CreateLongResourceData) GetSubject() string {
|
data.price = &dec
|
||||||
|
}
|
||||||
|
return *data.price, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *CreateLongResourceData) GetSubject() (string, error) {
|
||||||
if data.name == "" {
|
if data.name == "" {
|
||||||
var mode string
|
var mode string
|
||||||
switch data.Mode {
|
switch data.Mode {
|
||||||
case 1:
|
default:
|
||||||
|
return "", errors.New("无效的套餐模式")
|
||||||
|
|
||||||
|
case m.ResourceModeTime:
|
||||||
mode = "包时"
|
mode = "包时"
|
||||||
case 2:
|
case m.ResourceModeQuota:
|
||||||
mode = "包量"
|
mode = "包量"
|
||||||
}
|
}
|
||||||
|
|
||||||
data.name = fmt.Sprintf("长效动态%s %d 小时", mode, data.Live)
|
data.name = fmt.Sprintf("长效动态%s %d 小时", mode, data.Live)
|
||||||
}
|
}
|
||||||
return data.name
|
return data.name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *CreateLongResourceData) GetAmount() decimal.Decimal {
|
func (data *CreateLongResourceData) GetAmount() (decimal.Decimal, error) {
|
||||||
if data.price == nil {
|
if data.price == nil {
|
||||||
var factor int32 = 0
|
var factor int32 = 0
|
||||||
switch data.Mode {
|
switch data.Mode {
|
||||||
|
default:
|
||||||
|
return decimal.Zero, errors.New("无效的套餐模式")
|
||||||
|
|
||||||
case m.ResourceModeTime:
|
case m.ResourceModeTime:
|
||||||
factor = data.Expire * data.DailyLimit
|
if data.Expire == nil {
|
||||||
|
return decimal.Zero, errors.New("包时套餐过期时间不能为空")
|
||||||
|
}
|
||||||
|
factor = *data.Expire * data.Quota
|
||||||
case m.ResourceModeQuota:
|
case m.ResourceModeQuota:
|
||||||
factor = data.Quota
|
factor = data.Quota
|
||||||
}
|
}
|
||||||
|
|
||||||
var base int32
|
var base int32
|
||||||
switch data.Live {
|
switch data.Live {
|
||||||
|
default:
|
||||||
|
return decimal.Zero, errors.New("无效的套餐时长")
|
||||||
case 1:
|
case 1:
|
||||||
base = 30
|
base = 30
|
||||||
case 4:
|
case 4:
|
||||||
@@ -271,9 +330,13 @@ func (data *CreateLongResourceData) GetAmount() decimal.Decimal {
|
|||||||
var dec = decimal.Decimal{}.
|
var dec = decimal.Decimal{}.
|
||||||
Add(decimal.NewFromInt32(base * factor)).
|
Add(decimal.NewFromInt32(base * factor)).
|
||||||
Div(decimal.NewFromInt(100))
|
Div(decimal.NewFromInt(100))
|
||||||
|
if dec.IsZero() {
|
||||||
|
return decimal.Zero, errors.New("计算金额错误")
|
||||||
|
}
|
||||||
|
|
||||||
data.price = &dec
|
data.price = &dec
|
||||||
}
|
}
|
||||||
return *data.price
|
return *data.price, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourceOnTradeComplete struct{}
|
type ResourceOnTradeComplete struct{}
|
||||||
|
|||||||
@@ -35,12 +35,18 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
|
|||||||
platform := data.Platform
|
platform := data.Platform
|
||||||
method := data.Method
|
method := data.Method
|
||||||
tType := data.Product.GetType()
|
tType := data.Product.GetType()
|
||||||
subject := data.Product.GetSubject()
|
|
||||||
amount := data.Product.GetAmount()
|
|
||||||
expire := time.Now().Add(30 * time.Minute)
|
expire := time.Now().Add(30 * time.Minute)
|
||||||
|
subject, err := data.Product.GetSubject()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
amount, err := data.Product.GetAmount()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// 实际支付金额,只在创建真实订单时使用
|
// 实际支付金额,只在创建真实订单时使用
|
||||||
var amountReal = data.Product.GetAmount()
|
amountReal := amount
|
||||||
if env.RunMode == env.RunModeDev {
|
if env.RunMode == env.RunModeDev {
|
||||||
amountReal = decimal.NewFromFloat(0.01)
|
amountReal = decimal.NewFromFloat(0.01)
|
||||||
}
|
}
|
||||||
@@ -60,7 +66,7 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var expireAt = time.Time(u.Z(coupon.ExpireAt))
|
expireAt := time.Time(u.Z(coupon.ExpireAt))
|
||||||
if !expireAt.IsZero() && expireAt.Before(now) {
|
if !expireAt.IsZero() && expireAt.Before(now) {
|
||||||
_, err = q.Coupon.
|
_, err = q.Coupon.
|
||||||
Where(q.Coupon.ID.Eq(coupon.ID)).
|
Where(q.Coupon.ID.Eq(coupon.ID)).
|
||||||
@@ -99,7 +105,7 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成订单号
|
// 生成订单号
|
||||||
var tradeNo, err = ID.GenSerial()
|
tradeNo, err := ID.GenSerial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, core.NewServErr("生成订单号失败", err)
|
return nil, core.NewServErr("生成订单号失败", err)
|
||||||
}
|
}
|
||||||
@@ -692,8 +698,8 @@ type OnTradeCompletedData struct {
|
|||||||
|
|
||||||
type ProductInfo interface {
|
type ProductInfo interface {
|
||||||
GetType() m.TradeType
|
GetType() m.TradeType
|
||||||
GetSubject() string
|
GetSubject() (string, error)
|
||||||
GetAmount() decimal.Decimal
|
GetAmount() (decimal.Decimal, error)
|
||||||
Serialize() (string, error)
|
Serialize() (string, error)
|
||||||
Deserialize(str string) error
|
Deserialize(str string) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,15 @@ func (s *userService) UpdateBalanceByTrade(uid int32, info *RechargeProductInfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成账单
|
// 生成账单
|
||||||
err = q.Bill.Create(newForRecharge(uid, Bill.GenNo(), info.GetSubject(), info.GetAmount(), trade))
|
subject, err := info.GetSubject()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
amount, err := info.GetAmount()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = q.Bill.Create(newForRecharge(uid, Bill.GenNo(), subject, amount, trade))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("生成账单失败", err)
|
return core.NewServErr("生成账单失败", err)
|
||||||
}
|
}
|
||||||
@@ -39,23 +47,25 @@ func (s *userService) UpdateBalanceByTrade(uid int32, info *RechargeProductInfo,
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func updateBalance(q *q.Query, uid int32, info *RechargeProductInfo) (err error) {
|
func updateBalance(q *q.Query, uid int32, info *RechargeProductInfo) error {
|
||||||
|
|
||||||
// 更新余额
|
|
||||||
user, err := q.User.
|
user, err := q.User.
|
||||||
Where(q.User.ID.Eq(uid)).Take()
|
Where(q.User.ID.Eq(uid)).Take()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("查询用户失败", err)
|
return core.NewServErr("查询用户失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var amount = user.Balance.Add(info.GetAmount())
|
amount, err := info.GetAmount()
|
||||||
if amount.IsNegative() {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
balance := user.Balance.Add(amount)
|
||||||
|
if balance.IsNegative() {
|
||||||
return core.NewServErr("用户余额不足")
|
return core.NewServErr("用户余额不足")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = q.User.
|
_, err = q.User.
|
||||||
Where(q.User.ID.Eq(user.ID)).
|
Where(q.User.ID.Eq(user.ID)).
|
||||||
UpdateSimple(q.User.Balance.Value(amount))
|
UpdateSimple(q.User.Balance.Value(balance))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新用户余额失败", err)
|
return core.NewServErr("更新用户余额失败", err)
|
||||||
}
|
}
|
||||||
@@ -75,12 +85,13 @@ func (r *RechargeProductInfo) GetType() m.TradeType {
|
|||||||
return m.TradeTypeRecharge
|
return m.TradeTypeRecharge
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RechargeProductInfo) GetSubject() string {
|
func (r *RechargeProductInfo) GetSubject() (string, error) {
|
||||||
return fmt.Sprintf("账户充值 - %s元", r.GetAmount().StringFixed(2))
|
amount, _ := r.GetAmount()
|
||||||
|
return fmt.Sprintf("账户充值 - %s元", amount.StringFixed(2)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RechargeProductInfo) GetAmount() decimal.Decimal {
|
func (r *RechargeProductInfo) GetAmount() (decimal.Decimal, error) {
|
||||||
return decimal.NewFromInt(int64(r.Amount)).Div(decimal.NewFromInt(100))
|
return decimal.NewFromInt(int64(r.Amount)).Div(decimal.NewFromInt(100)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RechargeProductInfo) Serialize() (string, error) {
|
func (r *RechargeProductInfo) Serialize() (string, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user