调整交易和账单模型,完善支付与用户余额基础处理逻辑

This commit is contained in:
2025-04-09 16:34:41 +08:00
parent 4c47a71f30
commit 02897db890
10 changed files with 203 additions and 85 deletions

View File

@@ -713,18 +713,23 @@ create table trade (
on update cascade
on delete cascade,
inner_no varchar(255) not null unique,
outer_no varchar(255) not null unique,
outer_no varchar(255),
type int not null,
subject varchar(255) not null,
remark varchar(255),
amount decimal(12, 2) not null default 0,
payment decimal(12, 2) not null default 0,
method int not null,
status int not null default 0,
paid_at timestamp,
cancel_at timestamp,
created_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp,
deleted_at timestamp
);
create index trade_user_id_index on trade (user_id);
create index trade_outer_no_index on trade (outer_no);
create index trade_type_index on trade (type);
create index trade_status_index on trade (status);
create index trade_deleted_at_index on trade (deleted_at);
@@ -734,59 +739,19 @@ comment on column trade.id is '订单ID';
comment on column trade.user_id is '用户ID';
comment on column trade.inner_no is '内部订单号';
comment on column trade.outer_no is '外部订单号';
comment on column trade.type is '订单类型0-充值余额1-购买产品';
comment on column trade.subject is '订单主题';
comment on column trade.remark is '订单备注';
comment on column trade.amount is '订单总金额';
comment on column trade.payment is '支付金额';
comment on column trade.method is '支付方式1-支付宝2-微信';
comment on column trade.status is '订单状态0-待支付1-已支付2-已取消3-已退款';
comment on column trade.paid_at is '支付时间';
comment on column trade.cancel_at is '取消时间';
comment on column trade.created_at is '创建时间';
comment on column trade.updated_at is '更新时间';
comment on column trade.deleted_at is '删除时间';
-- bill
drop table if exists bill cascade;
create table bill (
id serial primary key,
user_id int not null references "user" (id)
on update cascade
on delete cascade,
trade_id int references trade (id) --
on update cascade --
on delete set null,
resource_id int references resource (id) --
on update cascade --
on delete set null,
bill_no varchar(255) not null unique,
type int not null,
info varchar(255),
amount decimal(12, 2) not null default 0,
payment decimal(12, 2) not null default 0,
created_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp,
deleted_at timestamp
);
create index bill_user_id_index on bill (user_id);
create index bill_trade_id_index on bill (trade_id);
create index bill_resource_id_index on bill (resource_id);
create index bill_type_index on bill (type);
create index bill_deleted_at_index on bill (deleted_at);
-- bill表字段注释
comment on table bill is '账单表';
comment on column bill.id is '账单ID';
comment on column bill.trade_id is '订单ID';
comment on column bill.user_id is '用户ID';
comment on column bill.resource_id is '套餐ID';
comment on column bill.bill_no is '易读账单号';
comment on column bill.info is '产品可读信息';
comment on column bill.type is '账单类型0-充值1-消费2-退款';
comment on column bill.amount is '总金额';
comment on column bill.payment is '支付金额';
comment on column bill.created_at is '创建时间';
comment on column bill.updated_at is '更新时间';
comment on column bill.deleted_at is '删除时间';
-- refund
drop table if exists refund cascade;
create table refund (
@@ -816,4 +781,52 @@ comment on column refund.created_at is '创建时间';
comment on column refund.updated_at is '更新时间';
comment on column refund.deleted_at is '删除时间';
-- bill
drop table if exists bill cascade;
create table bill (
id serial primary key,
user_id int not null references "user" (id)
on update cascade
on delete cascade,
trade_id int references trade (id) --
on update cascade --
on delete set null,
resource_id int references resource (id) --
on update cascade --
on delete set null,
refund_id int references refund (id) --
on update cascade --
on delete set null,
bill_no varchar(255) not null unique,
info varchar(255),
type int not null,
status int not null,
created_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp,
deleted_at timestamp
);
create index bill_user_id_index on bill (user_id);
create index bill_trade_id_index on bill (trade_id);
create index bill_resource_id_index on bill (resource_id);
create index bill_refund_id_index on bill (refund_id);
create index bill_bill_no_index on bill (bill_no);
create index bill_type_index on bill (type);
create index bill_status_index on bill (status);
create index bill_deleted_at_index on bill (deleted_at);
-- bill表字段注释
comment on table bill is '账单表';
comment on column bill.id is '账单ID';
comment on column bill.user_id is '用户ID';
comment on column bill.trade_id is '订单ID';
comment on column bill.resource_id is '套餐ID';
comment on column bill.refund_id is '退款ID';
comment on column bill.bill_no is '易读账单号';
comment on column bill.info is '产品可读信息';
comment on column bill.type is '账单类型0-充值1-消费2-退款';
comment on column bill.status is '账单状态0-未完成1-已完成2-已作废';
comment on column bill.created_at is '创建时间';
comment on column bill.updated_at is '更新时间';
comment on column bill.deleted_at is '删除时间';
-- endregion

View File

@@ -22,6 +22,7 @@ type LoginResp struct {
RefreshToken string `json:"refresh_token"`
Expires int64 `json:"expires"`
Auth services.AuthContext `json:"auth"`
Profile *models.User `json:"profile"`
}
func Login(c *fiber.Ctx) error {
@@ -105,10 +106,12 @@ func loginByPhone(c *fiber.Ctx, req *LoginReq) error {
return err
}
user.Password = ""
return c.JSON(LoginResp{
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
Expires: token.AccessTokenExpires.Unix(),
Auth: auth,
Profile: user,
})
}

View File

@@ -44,12 +44,21 @@ func CreateResourceByBalance(c *fiber.Ctx) error {
}
// 计算价格
var amount = 0
var payment = 0
var amount = 100
var payment = 100
// 检查余额
if user.Balance < float64(req.Quota)/100 {
return errors.New("余额不足")
if user.Balance < float64(amount)/100 {
return fiber.NewError(fiber.StatusBadRequest, "余额不足")
}
// 更新用户余额
user.Balance -= float64(payment)
_, err = q.User.
Where(q.User.ID.Eq(authContext.Payload.Id)).
Update(q.User.Balance, user.Balance)
if err != nil {
return err
}
// 创建资源
@@ -73,26 +82,18 @@ func CreateResourceByBalance(c *fiber.Ctx) error {
return err
}
// 更新用户余额
user.Balance -= float64(payment)
_, err = q.User.
Where(q.User.ID.Eq(authContext.Payload.Id)).
Update(q.User.Balance, user.Balance)
if err != nil {
return err
}
// 生成账单
bill := m.Bill{
UserID: authContext.Payload.Id,
ResourceID: resource.ID,
BillNo: services.ID.GenReadable("bil"),
Type: 1,
Info: "购买套餐",
Amount: float64(amount),
Payment: float64(payment),
Type: 1,
Status: 1,
}
err = q.Bill.Save(&bill)
err = q.Bill.
Omit(q.Bill.TradeID, q.Bill.RefundID).
Create(&bill)
if err != nil {
return err
}
@@ -103,7 +104,7 @@ func CreateResourceByBalance(c *fiber.Ctx) error {
return err
}
return errors.New("not implemented")
return nil
}
// endregion

View File

@@ -13,6 +13,7 @@ import (
// region CreateTrade
type CreateTradeReq struct {
Type int `json:"type" validate:"required"` // 交易类型1.充值2.购买
Subject string `json:"subject" validate:"required"`
Remark string `json:"remark"`
Amount int `json:"amount" validate:"required"`
@@ -32,7 +33,9 @@ func CreateTrade(c *fiber.Ctx) error {
return err
}
// 创建交易订单
// 调用外部接口
// 保存交易订单
num, err := services.ID.GenSerial(c.Context())
if err != nil {
return err
@@ -45,15 +48,37 @@ func CreateTrade(c *fiber.Ctx) error {
Amount: float64(req.Amount) / 100,
Method: int32(req.Method),
}
// 调用外部接口
// 保存交易订单
err = q.Trade.Create(&trade)
if err != nil {
return err
}
// 保存用户帐单
var info string
var t int32
switch req.Type {
case 1:
info = "充值余额"
t = 0
case 2:
info = "购买产品"
t = 1
}
bill := m.Bill{
UserID: authContext.Payload.Id,
TradeID: trade.ID,
BillNo: services.ID.GenReadable("bil"),
Info: info,
Type: t,
Status: 0,
}
err = q.Bill.
Omit(q.Bill.ResourceID, q.Bill.RefundID).
Create(&bill)
if err != nil {
return err
}
// 返回结果,外部支付链接
return nil
}

49
web/handlers/user.go Normal file
View File

@@ -0,0 +1,49 @@
package handlers
import (
"errors"
q "platform/web/queries"
"platform/web/services"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
// region GetUserByToken
type GetUserByTokenReq struct {
Token string `json:"token" validate:"required"`
}
// GetUserByToken 通过token获取用户信息
func GetUserByToken(c *fiber.Ctx) error {
// 解析请求参数
req := new(GetUserByTokenReq)
if err := c.BodyParser(req); err != nil {
return err
}
// 查询会话信息
session, err := services.Session.Find(c.Context(), req.Token)
if err != nil {
return err
}
// 查询用户信息
user, err := q.User.Debug().
Omit(q.User.Password, q.User.DeletedAt).
Where(q.User.ID.Eq(session.Payload.Id)).
Take()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "用户不存在")
}
return err
}
// 返回用户信息
return c.JSON(user)
}
// endregion

View File

@@ -17,8 +17,6 @@ type Bill struct {
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:账单ID" json:"id"` // 账单ID
UserID int32 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID
Info string `gorm:"column:info;comment:产品可读信息" json:"info"` // 产品可读信息
Amount float64 `gorm:"column:amount;not null;comment:总金额" json:"amount"` // 总金额
Payment float64 `gorm:"column:payment;not null;comment:支付金额" json:"payment"` // 支付金额
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
@@ -26,6 +24,8 @@ type Bill struct {
ResourceID int32 `gorm:"column:resource_id" json:"resource_id"`
Type int32 `gorm:"column:type;not null" json:"type"`
BillNo string `gorm:"column:bill_no;not null" json:"bill_no"`
RefundID int32 `gorm:"column:refund_id" json:"refund_id"`
Status int32 `gorm:"column:status;not null" json:"status"`
}
// TableName Bill's table name

View File

@@ -17,7 +17,7 @@ type Trade struct {
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:订单ID" json:"id"` // 订单ID
UserID int32 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID
InnerNo string `gorm:"column:inner_no;not null;comment:内部订单号" json:"inner_no"` // 内部订单号
OuterNo string `gorm:"column:outer_no;not null;comment:外部订单号" json:"outer_no"` // 外部订单号
OuterNo string `gorm:"column:outer_no;comment:外部订单号" json:"outer_no"` // 外部订单号
Subject string `gorm:"column:subject;not null;comment:订单主题" json:"subject"` // 订单主题
Remark string `gorm:"column:remark;comment:订单备注" json:"remark"` // 订单备注
Amount float64 `gorm:"column:amount;not null;comment:订单总金额" json:"amount"` // 订单总金额
@@ -27,6 +27,9 @@ type Trade struct {
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
Type int32 `gorm:"column:type;not null" json:"type"`
CancelAt time.Time `gorm:"column:cancel_at" json:"cancel_at"`
PaidAt time.Time `gorm:"column:paid_at" json:"paid_at"`
}
// TableName Trade's table name

View File

@@ -30,8 +30,6 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
_bill.ID = field.NewInt32(tableName, "id")
_bill.UserID = field.NewInt32(tableName, "user_id")
_bill.Info = field.NewString(tableName, "info")
_bill.Amount = field.NewFloat64(tableName, "amount")
_bill.Payment = field.NewFloat64(tableName, "payment")
_bill.CreatedAt = field.NewTime(tableName, "created_at")
_bill.UpdatedAt = field.NewTime(tableName, "updated_at")
_bill.DeletedAt = field.NewField(tableName, "deleted_at")
@@ -39,6 +37,8 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
_bill.ResourceID = field.NewInt32(tableName, "resource_id")
_bill.Type = field.NewInt32(tableName, "type")
_bill.BillNo = field.NewString(tableName, "bill_no")
_bill.RefundID = field.NewInt32(tableName, "refund_id")
_bill.Status = field.NewInt32(tableName, "status")
_bill.fillFieldMap()
@@ -49,18 +49,18 @@ type bill struct {
billDo
ALL field.Asterisk
ID field.Int32 // 账单ID
UserID field.Int32 // 用户ID
Info field.String // 产品可读信息
Amount field.Float64 // 总金额
Payment field.Float64 // 支付金额
CreatedAt field.Time // 创建时间
UpdatedAt field.Time // 更新时间
DeletedAt field.Field // 删除时间
ID field.Int32 // 账单ID
UserID field.Int32 // 用户ID
Info field.String // 产品可读信息
CreatedAt field.Time // 创建时间
UpdatedAt field.Time // 更新时间
DeletedAt field.Field // 删除时间
TradeID field.Int32
ResourceID field.Int32
Type field.Int32
BillNo field.String
RefundID field.Int32
Status field.Int32
fieldMap map[string]field.Expr
}
@@ -80,8 +80,6 @@ func (b *bill) updateTableName(table string) *bill {
b.ID = field.NewInt32(table, "id")
b.UserID = field.NewInt32(table, "user_id")
b.Info = field.NewString(table, "info")
b.Amount = field.NewFloat64(table, "amount")
b.Payment = field.NewFloat64(table, "payment")
b.CreatedAt = field.NewTime(table, "created_at")
b.UpdatedAt = field.NewTime(table, "updated_at")
b.DeletedAt = field.NewField(table, "deleted_at")
@@ -89,6 +87,8 @@ func (b *bill) updateTableName(table string) *bill {
b.ResourceID = field.NewInt32(table, "resource_id")
b.Type = field.NewInt32(table, "type")
b.BillNo = field.NewString(table, "bill_no")
b.RefundID = field.NewInt32(table, "refund_id")
b.Status = field.NewInt32(table, "status")
b.fillFieldMap()
@@ -109,8 +109,6 @@ func (b *bill) fillFieldMap() {
b.fieldMap["id"] = b.ID
b.fieldMap["user_id"] = b.UserID
b.fieldMap["info"] = b.Info
b.fieldMap["amount"] = b.Amount
b.fieldMap["payment"] = b.Payment
b.fieldMap["created_at"] = b.CreatedAt
b.fieldMap["updated_at"] = b.UpdatedAt
b.fieldMap["deleted_at"] = b.DeletedAt
@@ -118,6 +116,8 @@ func (b *bill) fillFieldMap() {
b.fieldMap["resource_id"] = b.ResourceID
b.fieldMap["type"] = b.Type
b.fieldMap["bill_no"] = b.BillNo
b.fieldMap["refund_id"] = b.RefundID
b.fieldMap["status"] = b.Status
}
func (b bill) clone(db *gorm.DB) bill {

View File

@@ -40,6 +40,9 @@ func newTrade(db *gorm.DB, opts ...gen.DOOption) trade {
_trade.CreatedAt = field.NewTime(tableName, "created_at")
_trade.UpdatedAt = field.NewTime(tableName, "updated_at")
_trade.DeletedAt = field.NewField(tableName, "deleted_at")
_trade.Type = field.NewInt32(tableName, "type")
_trade.CancelAt = field.NewTime(tableName, "cancel_at")
_trade.PaidAt = field.NewTime(tableName, "paid_at")
_trade.fillFieldMap()
@@ -63,6 +66,9 @@ type trade struct {
CreatedAt field.Time // 创建时间
UpdatedAt field.Time // 更新时间
DeletedAt field.Field // 删除时间
Type field.Int32
CancelAt field.Time
PaidAt field.Time
fieldMap map[string]field.Expr
}
@@ -92,6 +98,9 @@ func (t *trade) updateTableName(table string) *trade {
t.CreatedAt = field.NewTime(table, "created_at")
t.UpdatedAt = field.NewTime(table, "updated_at")
t.DeletedAt = field.NewField(table, "deleted_at")
t.Type = field.NewInt32(table, "type")
t.CancelAt = field.NewTime(table, "cancel_at")
t.PaidAt = field.NewTime(table, "paid_at")
t.fillFieldMap()
@@ -108,7 +117,7 @@ func (t *trade) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (t *trade) fillFieldMap() {
t.fieldMap = make(map[string]field.Expr, 13)
t.fieldMap = make(map[string]field.Expr, 16)
t.fieldMap["id"] = t.ID
t.fieldMap["user_id"] = t.UserID
t.fieldMap["inner_no"] = t.InnerNo
@@ -122,6 +131,9 @@ func (t *trade) fillFieldMap() {
t.fieldMap["created_at"] = t.CreatedAt
t.fieldMap["updated_at"] = t.UpdatedAt
t.fieldMap["deleted_at"] = t.DeletedAt
t.fieldMap["type"] = t.Type
t.fieldMap["cancel_at"] = t.CancelAt
t.fieldMap["paid_at"] = t.PaidAt
}
func (t trade) clone(db *gorm.DB) trade {

View File

@@ -28,6 +28,18 @@ func ApplyRouters(app *fiber.App) {
whitelist.Post("/update", handlers.UpdateWhitelist)
whitelist.Post("/remove", handlers.RemoveWhitelist)
// 资源
resource := api.Group("/resource")
resource.Post("/create/balance", handlers.CreateResourceByBalance)
// 用户
user := api.Group("/user")
user.Post("/get/token", handlers.GetUserByToken)
// 支付
trade := api.Group("/trade")
trade.Post("/create", handlers.CreateTrade)
// 临时
app.Get("/collect", handlers.CreateChannelGet)
app.Get("/temp", handlers.Temp)