From 02897db8908ea19c60b944098f952b1e984d8d50 Mon Sep 17 00:00:00 2001 From: luorijun Date: Wed, 9 Apr 2025 16:34:41 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BA=A4=E6=98=93=E5=92=8C?= =?UTF-8?q?=E8=B4=A6=E5=8D=95=E6=A8=A1=E5=9E=8B=EF=BC=8C=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E4=B8=8E=E7=94=A8=E6=88=B7=E4=BD=99=E9=A2=9D?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/sql/init.sql | 101 ++++++++++++++++++++++----------------- web/handlers/login.go | 3 ++ web/handlers/resource.go | 37 +++++++------- web/handlers/trade.go | 35 ++++++++++++-- web/handlers/user.go | 49 +++++++++++++++++++ web/models/bill.gen.go | 4 +- web/models/trade.gen.go | 5 +- web/queries/bill.gen.go | 28 +++++------ web/queries/trade.gen.go | 14 +++++- web/router.go | 12 +++++ 10 files changed, 203 insertions(+), 85 deletions(-) create mode 100644 web/handlers/user.go diff --git a/scripts/sql/init.sql b/scripts/sql/init.sql index d0ff34a..6bb2ee4 100644 --- a/scripts/sql/init.sql +++ b/scripts/sql/init.sql @@ -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 \ No newline at end of file diff --git a/web/handlers/login.go b/web/handlers/login.go index c85883a..0281827 100644 --- a/web/handlers/login.go +++ b/web/handlers/login.go @@ -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, }) } diff --git a/web/handlers/resource.go b/web/handlers/resource.go index 8d4a918..c1cbfbd 100644 --- a/web/handlers/resource.go +++ b/web/handlers/resource.go @@ -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 diff --git a/web/handlers/trade.go b/web/handlers/trade.go index 64e5e1f..663d91b 100644 --- a/web/handlers/trade.go +++ b/web/handlers/trade.go @@ -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 } diff --git a/web/handlers/user.go b/web/handlers/user.go new file mode 100644 index 0000000..75fd5da --- /dev/null +++ b/web/handlers/user.go @@ -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 diff --git a/web/models/bill.gen.go b/web/models/bill.gen.go index b0164d4..fe24302 100644 --- a/web/models/bill.gen.go +++ b/web/models/bill.gen.go @@ -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 diff --git a/web/models/trade.gen.go b/web/models/trade.gen.go index 119f0f5..35ee95a 100644 --- a/web/models/trade.gen.go +++ b/web/models/trade.gen.go @@ -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 diff --git a/web/queries/bill.gen.go b/web/queries/bill.gen.go index 1edb685..20383a0 100644 --- a/web/queries/bill.gen.go +++ b/web/queries/bill.gen.go @@ -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 { diff --git a/web/queries/trade.gen.go b/web/queries/trade.gen.go index c75d65a..6d4fc51 100644 --- a/web/queries/trade.gen.go +++ b/web/queries/trade.gen.go @@ -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 { diff --git a/web/router.go b/web/router.go index 60cb28f..4811b9b 100644 --- a/web/router.go +++ b/web/router.go @@ -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)