小程序虚拟支付
This commit is contained in:
@@ -31,16 +31,18 @@ type MyInfoRes struct {
|
||||
// ===== 用户管理 =====
|
||||
|
||||
type AdminUserItem struct {
|
||||
Id int64 `json:"id"`
|
||||
Nickname string `json:"nickname"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
Openid string `json:"openid"`
|
||||
LevelKey string `json:"level_key"`
|
||||
LevelName string `json:"level_name"`
|
||||
Status int `json:"status"`
|
||||
UsageCount int64 `json:"usage_count"`
|
||||
LastUsedAt string `json:"last_used_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Id int64 `json:"id"`
|
||||
Nickname string `json:"nickname"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
Openid string `json:"openid"`
|
||||
LevelKey string `json:"level_key"`
|
||||
LevelName string `json:"level_name"`
|
||||
LevelExpireAt string `json:"level_expire_at"`
|
||||
QuotaPeriodDays int `json:"quota_period_days"` // 0 = 跟随全局设置
|
||||
Status int `json:"status"`
|
||||
UsageCount int64 `json:"usage_count"`
|
||||
LastUsedAt string `json:"last_used_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
type UserListReq struct {
|
||||
@@ -141,6 +143,9 @@ type AdminToolItem struct {
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Description string `json:"description"`
|
||||
FreeQuota int `json:"free_quota"` // 免费额度(每周期次数)
|
||||
MemberQuota int `json:"member_quota"` // 会员额度(每周期次数)
|
||||
PayCost int `json:"pay_cost"` // 付费额度每次消耗系数
|
||||
Sort int `json:"sort"`
|
||||
IsEnabled int `json:"is_enabled"`
|
||||
IsHot int `json:"is_hot"`
|
||||
@@ -161,13 +166,16 @@ type ToolsListRes struct {
|
||||
}
|
||||
|
||||
type ToolsSaveReq struct {
|
||||
g.Meta `path:"/tools/save" method:"post" tags:"admin" summary:"保存工具"`
|
||||
g.Meta `path:"/tools/save" method:"post" tags:"admin" summary:"保存工具(含额度配置)"`
|
||||
Id int64 `json:"id"`
|
||||
ToolKey string `v:"required-if:id,0#工具标识不能为空" json:"toolKey"`
|
||||
ModuleKey string `v:"required#所属模块不能为空" json:"moduleKey"`
|
||||
Name string `v:"required#工具名称不能为空" json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Description string `json:"description"`
|
||||
FreeQuota int `v:"min:0#免费额度不能为负" json:"freeQuota"`
|
||||
MemberQuota int `v:"min:0#会员额度不能为负" json:"memberQuota"`
|
||||
PayCost int `v:"min:1#付费消耗系数至少为 1" json:"payCost"`
|
||||
Sort int `json:"sort"`
|
||||
IsEnabled int `json:"isEnabled"`
|
||||
IsHot int `json:"isHot"`
|
||||
|
||||
125
api/admin/v1/quota.go
Normal file
125
api/admin/v1/quota.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 管理端:额度配置 / 次数包 / 会员套餐 / 额度设置
|
||||
// ============================================================================
|
||||
|
||||
// ===== 次数包档位 =====
|
||||
|
||||
type QuotaPackItem struct {
|
||||
Id int64 `json:"id"`
|
||||
PackKey string `json:"pack_key"`
|
||||
Name string `json:"name"`
|
||||
Times int `json:"times"`
|
||||
PriceCents int64 `json:"price_cents"`
|
||||
ProductId string `json:"product_id"`
|
||||
ValidDays int `json:"valid_days"`
|
||||
ToolKey string `json:"tool_key"`
|
||||
ToolName string `json:"tool_name"`
|
||||
Badge string `json:"badge"`
|
||||
Sort int `json:"sort"`
|
||||
IsEnabled int `json:"is_enabled"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type QuotaPackListReq struct {
|
||||
g.Meta `path:"/quota/pack/list" method:"get" tags:"admin" summary:"次数包档位列表"`
|
||||
}
|
||||
type QuotaPackListRes struct {
|
||||
List []QuotaPackItem `json:"list"`
|
||||
}
|
||||
|
||||
type QuotaPackSaveReq struct {
|
||||
g.Meta `path:"/quota/pack/save" method:"post" tags:"admin" summary:"保存次数包档位"`
|
||||
Id int64 `json:"id"`
|
||||
PackKey string `v:"required-if:id,0#档位标识不能为空" json:"packKey"`
|
||||
Name string `v:"required#展示名不能为空" json:"name"`
|
||||
Times int `v:"min:1#次数必须大于 0" json:"times"`
|
||||
PriceCents int64 `v:"min:1#价格必须大于 0" json:"priceCents"`
|
||||
ProductId string `v:"required#微信道具 ID 不能为空" json:"productId"`
|
||||
ValidDays int `json:"validDays"`
|
||||
ToolKey string `json:"toolKey"`
|
||||
Badge string `json:"badge"`
|
||||
Sort int `json:"sort"`
|
||||
IsEnabled int `json:"isEnabled"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type QuotaPackSaveRes struct{}
|
||||
|
||||
type QuotaPackToggleReq struct {
|
||||
g.Meta `path:"/quota/pack/toggle" method:"post" tags:"admin" summary:"次数包启停"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
type QuotaPackToggleRes struct{}
|
||||
|
||||
// ===== 额度设置 =====
|
||||
|
||||
type QuotaSettingGetReq struct {
|
||||
g.Meta `path:"/quota/setting/get" method:"get" tags:"admin" summary:"额度设置"`
|
||||
}
|
||||
type QuotaSettingGetRes struct {
|
||||
PeriodDays int `json:"period_days"` // 全局默认额度重置周期(天)
|
||||
DefaultPeriodDays int `json:"default_period_days"` // 代码兜底值,界面上作为占位提示
|
||||
}
|
||||
|
||||
type QuotaSettingSaveReq struct {
|
||||
g.Meta `path:"/quota/setting/save" method:"post" tags:"admin" summary:"保存额度设置"`
|
||||
PeriodDays int `v:"min:1|max:3650#周期至少 1 天|周期最多 3650 天" json:"periodDays"`
|
||||
}
|
||||
type QuotaSettingSaveRes struct{}
|
||||
|
||||
// ===== 会员套餐 =====
|
||||
|
||||
type PlanItem struct {
|
||||
Id int64 `json:"id"`
|
||||
PlanKey string `json:"plan_key"`
|
||||
Name string `json:"name"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
LevelKey string `json:"level_key"`
|
||||
LevelName string `json:"level_name"`
|
||||
DurationDays int `json:"duration_days"`
|
||||
PriceCents int64 `json:"price_cents"`
|
||||
ProductId string `json:"product_id"`
|
||||
Badge string `json:"badge"`
|
||||
QuotaPeriodDays int `json:"quota_period_days"`
|
||||
Sort int `json:"sort"`
|
||||
IsEnabled int `json:"is_enabled"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type PlanListReq struct {
|
||||
g.Meta `path:"/plan/list" method:"get" tags:"admin" summary:"会员套餐列表"`
|
||||
}
|
||||
type PlanListRes struct {
|
||||
List []PlanItem `json:"list"`
|
||||
}
|
||||
|
||||
// PlanSaveReq 只开放运营真正需要改的字段:价格、道具 ID、额度周期、上下架。
|
||||
// plan_key / level_key / duration_days 属跨端契约,仍由 seedMemberPlans 定义,
|
||||
// 避免后台改出「前端不认识的等级」。
|
||||
type PlanSaveReq struct {
|
||||
g.Meta `path:"/plan/save" method:"post" tags:"admin" summary:"保存会员套餐(仅价格/道具/额度周期/上下架)"`
|
||||
Id int64 `v:"required#套餐 ID 不能为空" json:"id"`
|
||||
Name string `v:"required#套餐名不能为空" json:"name"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
PriceCents int64 `v:"min:1#价格必须大于 0" json:"priceCents"`
|
||||
ProductId string `v:"required#微信道具 ID 不能为空" json:"productId"`
|
||||
Badge string `json:"badge"`
|
||||
QuotaPeriodDays int `json:"quotaPeriodDays"`
|
||||
Sort int `json:"sort"`
|
||||
IsEnabled int `json:"isEnabled"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type PlanSaveRes struct{}
|
||||
|
||||
// ===== 用户额度周期 =====
|
||||
|
||||
type UserQuotaPeriodReq struct {
|
||||
g.Meta `path:"/user/quota-period" method:"post" tags:"admin" summary:"单独调整用户额度周期"`
|
||||
UserId int64 `v:"required#用户 ID 不能为空" json:"userId"`
|
||||
PeriodDays int `v:"min:0|max:3650#周期不能为负|周期最多 3650 天" json:"periodDays"`
|
||||
}
|
||||
type UserQuotaPeriodRes struct{}
|
||||
107
api/user/v1/member.go
Normal file
107
api/user/v1/member.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 会员购买(微信小程序虚拟支付 · 道具直购)
|
||||
// ============================================================================
|
||||
|
||||
// MemberPlanOut 可购买的会员套餐
|
||||
type MemberPlanOut struct {
|
||||
PlanKey string `json:"plan_key"`
|
||||
Name string `json:"name"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
LevelKey string `json:"level_key"`
|
||||
DurationDays int `json:"duration_days"`
|
||||
PriceCents int64 `json:"price_cents"` // 单位:分
|
||||
ProductId string `json:"product_id"`
|
||||
Badge string `json:"badge"`
|
||||
}
|
||||
|
||||
// MemberLevelOut 等级权益(用于前端渲染权益对比)
|
||||
type MemberLevelOut struct {
|
||||
LevelKey string `json:"level_key"`
|
||||
Name string `json:"name"`
|
||||
Modules []string `json:"modules"`
|
||||
ModuleNames []string `json:"module_names"`
|
||||
IsCurrent bool `json:"is_current"`
|
||||
}
|
||||
|
||||
// MemberStateOut 我的会员状态
|
||||
type MemberStateOut struct {
|
||||
IsMember bool `json:"is_member"`
|
||||
LevelKey string `json:"level_key"`
|
||||
LevelName string `json:"level_name"`
|
||||
ExpireAt string `json:"expire_at"` // 空串表示永久或未开通
|
||||
DaysLeft int `json:"days_left"`
|
||||
Modules []string `json:"modules"`
|
||||
}
|
||||
|
||||
// MemberCenterReq 会员中心
|
||||
type MemberCenterReq struct {
|
||||
g.Meta `path:"/member/center" method:"get" tags:"member" summary:"会员中心(套餐 + 会员状态 + 权益)"`
|
||||
}
|
||||
type MemberCenterRes struct {
|
||||
Plans []MemberPlanOut `json:"plans"`
|
||||
Levels []MemberLevelOut `json:"levels"`
|
||||
State MemberStateOut `json:"state"`
|
||||
Notices []string `json:"notices"`
|
||||
XpayReady bool `json:"xpay_ready"` // 服务端虚拟支付参数是否已配置
|
||||
Quotas []QuotaToolOut `json:"quotas"` // 各工具额度(会员权益页据此展示提升幅度)
|
||||
Packs []QuotaPackOut `json:"packs"` // 次数包通用档位
|
||||
PeriodDays int `json:"period_days"` // 当前生效的额度重置周期
|
||||
}
|
||||
|
||||
// MemberOrderCreateReq 创建会员订单
|
||||
//
|
||||
// Code 为 wx.login 获取的临时登录凭证 —— 服务端用它换 session_key,
|
||||
// session_key 是「用户态签名 signature」的密钥;同时校验支付人与登录人一致。
|
||||
type MemberOrderCreateReq struct {
|
||||
g.Meta `path:"/member/order" method:"post" tags:"member" summary:"创建会员订单(返回支付参数)"`
|
||||
PlanKey string `v:"required#请选择会员套餐" json:"plan_key"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
type MemberOrderCreateRes struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Mode string `json:"mode"` // 固定 short_series_goods
|
||||
SignData string `json:"signData"` // JSON 字符串,必须原样传给 wx.requestVirtualPayment
|
||||
PaySig string `json:"paySig"`
|
||||
Signature string `json:"signature"`
|
||||
Env int `json:"env"`
|
||||
}
|
||||
|
||||
// MemberOrderCheckReq 查询订单状态(前端支付成功回调后轮询;同时承担兜底发货)
|
||||
type MemberOrderCheckReq struct {
|
||||
g.Meta `path:"/member/order/check" method:"post" tags:"member" summary:"查询会员订单状态"`
|
||||
OutTradeNo string `v:"required#订单号不能为空" json:"out_trade_no"`
|
||||
}
|
||||
type MemberOrderCheckRes struct {
|
||||
Status int `json:"status"`
|
||||
Delivered bool `json:"delivered"`
|
||||
State MemberStateOut `json:"state"`
|
||||
}
|
||||
|
||||
// MemberOrdersReq 我的会员订单
|
||||
type MemberOrdersReq struct {
|
||||
g.Meta `path:"/member/orders" method:"get" tags:"member" summary:"我的会员订单"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
type MemberOrderOut struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
OrderType int `json:"order_type"` // 1=会员套餐 2=次数包
|
||||
PlanKey string `json:"plan_key"`
|
||||
PlanName string `json:"plan_name"`
|
||||
Title string `json:"title"` // 统一展示标题(套餐名 / 次数包名)
|
||||
ToolKey string `json:"tool_key"`
|
||||
ToolName string `json:"tool_name"`
|
||||
Times int `json:"times"`
|
||||
PriceCents int64 `json:"price_cents"`
|
||||
Status int `json:"status"`
|
||||
StatusText string `json:"status_text"`
|
||||
PayChannel string `json:"pay_channel"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
DeliveredAt string `json:"delivered_at"`
|
||||
}
|
||||
type MemberOrdersRes struct {
|
||||
List []MemberOrderOut `json:"list"`
|
||||
}
|
||||
108
api/user/v1/quota.go
Normal file
108
api/user/v1/quota.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 工具额度与次数包
|
||||
//
|
||||
// 额度三池:免费 / 会员 / 付费(次数包),扣减顺序 免费 → 会员 → 付费。
|
||||
// 周期为滚动窗口,默认 7 天,后台可给「全局 / 会员套餐 / 单个用户」分别调整。
|
||||
// ============================================================================
|
||||
|
||||
// QuotaToolOut 单个工具的额度状态
|
||||
type QuotaToolOut struct {
|
||||
ToolKey string `json:"tool_key"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
FreeTotal int `json:"free_total"`
|
||||
FreeUsed int `json:"free_used"`
|
||||
FreeLeft int `json:"free_left"`
|
||||
MemberTotal int `json:"member_total"`
|
||||
MemberUsed int `json:"member_used"`
|
||||
MemberLeft int `json:"member_left"`
|
||||
PaidLeft int `json:"paid_left"` // 付费额度余额(次数)
|
||||
PaidExpireAt string `json:"paid_expire_at"` // 付费额度到期日 yyyy-MM-dd
|
||||
PayCost int `json:"pay_cost"` // 每次使用消耗的付费额度系数
|
||||
IsMember bool `json:"is_member"`
|
||||
PeriodDays int `json:"period_days"`
|
||||
PeriodEnd string `json:"period_end"` // 当前周期结束时间
|
||||
// Source 下一次扣减会命中的池:free / member / paid / none
|
||||
Source string `json:"source"`
|
||||
CanUse bool `json:"can_use"`
|
||||
TotalLeft int `json:"total_left"` // 本周期内还能用几次(付费额度按系数折算)
|
||||
Message string `json:"message"` // 额度用尽时的可读原因
|
||||
}
|
||||
|
||||
// QuotaPackOut 次数包档位
|
||||
type QuotaPackOut struct {
|
||||
PackKey string `json:"pack_key"`
|
||||
Name string `json:"name"`
|
||||
Times int `json:"times"`
|
||||
PriceCents int64 `json:"price_cents"`
|
||||
ValidDays int `json:"valid_days"`
|
||||
Badge string `json:"badge"`
|
||||
ToolKey string `json:"tool_key"` // 空 = 通用档位(购买时选择工具)
|
||||
UnitPriceCents int64 `json:"unit_price_cents"` // 每 100 次的均价(分),服务端算好避免前端浮点误差
|
||||
}
|
||||
|
||||
// QuotaMyReq 我的额度总览
|
||||
type QuotaMyReq struct {
|
||||
g.Meta `path:"/quota/my" method:"get" tags:"quota" summary:"我的工具额度总览"`
|
||||
}
|
||||
type QuotaMyRes struct {
|
||||
PeriodDays int `json:"period_days"`
|
||||
IsMember bool `json:"is_member"`
|
||||
LevelKey string `json:"level_key"`
|
||||
LevelName string `json:"level_name"`
|
||||
LevelExpireAt string `json:"level_expire_at"`
|
||||
Tools []QuotaToolOut `json:"tools"`
|
||||
TotalLeft int `json:"total_left"`
|
||||
Packs []QuotaPackOut `json:"packs"`
|
||||
}
|
||||
|
||||
// QuotaToolReq 单工具额度(进入工具页时查询,用于额度条与拦截提示)
|
||||
type QuotaToolReq struct {
|
||||
g.Meta `path:"/quota/tool" method:"get" tags:"quota" summary:"单个工具的额度状态"`
|
||||
ToolKey string `v:"required#工具标识不能为空" json:"tool_key"`
|
||||
}
|
||||
type QuotaToolRes struct {
|
||||
Tool *QuotaToolOut `json:"tool"`
|
||||
}
|
||||
|
||||
// QuotaConsumeReq 扣减一次额度(在「产出结果」时调用,而不是打开页面时)
|
||||
type QuotaConsumeReq struct {
|
||||
g.Meta `path:"/quota/consume" method:"post" tags:"quota" summary:"扣减工具额度"`
|
||||
ToolKey string `v:"required#工具标识不能为空" json:"tool_key"`
|
||||
}
|
||||
type QuotaConsumeRes struct {
|
||||
Tool *QuotaToolOut `json:"tool"`
|
||||
}
|
||||
|
||||
// QuotaPacksReq 某工具可购买的次数包档位
|
||||
type QuotaPacksReq struct {
|
||||
g.Meta `path:"/quota/packs" method:"get" tags:"quota" summary:"次数包档位(按工具)"`
|
||||
ToolKey string `json:"tool_key"`
|
||||
}
|
||||
type QuotaPacksRes struct {
|
||||
List []QuotaPackOut `json:"list"`
|
||||
}
|
||||
|
||||
// QuotaOrderCreateReq 创建次数包订单(支付参数与会员套餐完全同构)
|
||||
type QuotaOrderCreateReq struct {
|
||||
g.Meta `path:"/quota/order" method:"post" tags:"quota" summary:"创建次数包订单(返回支付参数)"`
|
||||
PackKey string `v:"required#请选择次数包" json:"pack_key"`
|
||||
ToolKey string `v:"required#请选择要购买的次数包工具" json:"tool_key"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
type QuotaOrderCreateRes struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Mode string `json:"mode"`
|
||||
SignData string `json:"signData"`
|
||||
PaySig string `json:"paySig"`
|
||||
Signature string `json:"signature"`
|
||||
Env int `json:"env"`
|
||||
PackKey string `json:"pack_key"`
|
||||
ToolKey string `json:"tool_key"`
|
||||
Times int `json:"times"`
|
||||
ValidDays int `json:"valid_days"`
|
||||
}
|
||||
@@ -100,13 +100,14 @@ type ModuleOut struct {
|
||||
}
|
||||
|
||||
type ToolOut struct {
|
||||
ToolKey string `json:"tool_key"`
|
||||
ModuleKey string `json:"module_key"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Description string `json:"description"`
|
||||
IsHot bool `json:"is_hot"`
|
||||
Locked bool `json:"locked"`
|
||||
ToolKey string `json:"tool_key"`
|
||||
ModuleKey string `json:"module_key"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Description string `json:"description"`
|
||||
IsHot bool `json:"is_hot"`
|
||||
Locked bool `json:"locked"`
|
||||
Quota *QuotaToolOut `json:"quota"` // 额度状态(免费/会员/付费),未登录或查询失败时为 null
|
||||
}
|
||||
|
||||
type ToolsListReq struct {
|
||||
|
||||
Reference in New Issue
Block a user