优化支付功能
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
"server/services"
|
||||
"server/services"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
@@ -59,8 +58,6 @@ type moduleWithEnabled struct {
|
||||
// 返回当前 backend 账号可见的模块(status=1 且 is_show=1),并根据租户绑定的套餐标记每个模块是否已开通。
|
||||
// 前端可据此排序(已开通在前)、置灰未开通卡片、阻止跳转。
|
||||
func (c *BackendModulesController) GetTenantList() {
|
||||
claims, err := c.backendModulesClaims()
|
||||
if err != nil {
|
||||
claims, err := c.backendModulesClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
@@ -68,7 +65,6 @@ func (c *BackendModulesController) GetTenantList() {
|
||||
}
|
||||
|
||||
var rows []models.SystemModules
|
||||
_, err = models.Orm.QueryTable(new(models.SystemModules)).
|
||||
_, err = models.Orm.QueryTable(new(models.SystemModules)).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("status", 1).
|
||||
|
||||
@@ -310,3 +310,218 @@ func (c *BackendProductPurchaseController) GetMyOrders() {
|
||||
}
|
||||
c.jsonOK(map[string]interface{}{"list": rows, "total": len(rows)}, "获取成功")
|
||||
}
|
||||
|
||||
// ============================ 套餐购买 ============================
|
||||
|
||||
// GetPackageList GET /backend/package/list
|
||||
// 租户端可购买套餐列表(仅启用的套餐)
|
||||
func (c *BackendProductPurchaseController) GetPackageList() {
|
||||
if _, err := c.claims(); err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
var rows []models.SystemTenantPackage
|
||||
if _, err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("status", 1).
|
||||
OrderBy("sort", "id").
|
||||
All(&rows); err != nil {
|
||||
c.jsonErr(500, 500, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ids := make([]uint64, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
ids = append(ids, r.ID)
|
||||
}
|
||||
moduleMap := loadPackageModules(ids)
|
||||
|
||||
list := make([]map[string]interface{}, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
modules := moduleMap[r.ID]
|
||||
if modules == nil {
|
||||
modules = []tenantPackageModuleItem{}
|
||||
}
|
||||
list = append(list, map[string]interface{}{
|
||||
"id": r.ID,
|
||||
"name": r.Name,
|
||||
"code": r.Code,
|
||||
"description": r.Description,
|
||||
"price": r.Price,
|
||||
"duration_days": r.DurationDays,
|
||||
"user_quota": r.UserQuota,
|
||||
"extra_user_price": r.ExtraUserPrice,
|
||||
"is_default": r.IsDefault,
|
||||
"sort": r.Sort,
|
||||
"status": r.Status,
|
||||
"remark": r.Remark,
|
||||
"modules": modules,
|
||||
"module_codes": moduleCodes(modules),
|
||||
"create_time": r.CreateTime,
|
||||
"update_time": r.UpdateTime,
|
||||
})
|
||||
}
|
||||
c.jsonOK(map[string]interface{}{"list": list, "total": len(list)}, "获取成功")
|
||||
}
|
||||
|
||||
// GetPackageDetailByCode GET /backend/package/detail/:code
|
||||
// 按套餐编码获取详情(租户端购买页使用)
|
||||
func (c *BackendProductPurchaseController) GetPackageDetailByCode() {
|
||||
if _, err := c.claims(); err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
code := strings.TrimSpace(c.Ctx.Input.Param(":code"))
|
||||
if code == "" {
|
||||
c.jsonErr(400, 400, "套餐编码不能为空")
|
||||
return
|
||||
}
|
||||
var row models.SystemTenantPackage
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
||||
Filter("code", code).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("status", 1).
|
||||
One(&row); err != nil {
|
||||
c.jsonErr(404, 404, "套餐不存在或已下架")
|
||||
return
|
||||
}
|
||||
modules := loadPackageModules([]uint64{row.ID})[row.ID]
|
||||
if modules == nil {
|
||||
modules = []tenantPackageModuleItem{}
|
||||
}
|
||||
c.jsonOK(map[string]interface{}{
|
||||
"id": row.ID,
|
||||
"name": row.Name,
|
||||
"code": row.Code,
|
||||
"description": row.Description,
|
||||
"price": row.Price,
|
||||
"duration_days": row.DurationDays,
|
||||
"user_quota": row.UserQuota,
|
||||
"extra_user_price": row.ExtraUserPrice,
|
||||
"is_default": row.IsDefault,
|
||||
"sort": row.Sort,
|
||||
"status": row.Status,
|
||||
"remark": row.Remark,
|
||||
"modules": modules,
|
||||
"module_codes": moduleCodes(modules),
|
||||
"create_time": row.CreateTime,
|
||||
"update_time": row.UpdateTime,
|
||||
}, "获取成功")
|
||||
}
|
||||
|
||||
type createPackageOrderPayload struct {
|
||||
PackageCode string `json:"package_code"`
|
||||
Channel string `json:"channel"`
|
||||
PayType string `json:"pay_type"`
|
||||
ReturnURL string `json:"return_url"`
|
||||
}
|
||||
|
||||
// CreatePackageOrder POST /backend/package/order/create
|
||||
// 创建套餐购买订单;channel 非空时同时创建支付单并返回拉起支付参数。
|
||||
// 订单绑定 PackageID,支付成功后自动为租户开通对应套餐。
|
||||
func (c *BackendProductPurchaseController) CreatePackageOrder() {
|
||||
claims, err := c.claims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
tid := uint64(claims.TenantId)
|
||||
if tid == 0 {
|
||||
c.jsonErr(401, 401, "未获取到租户信息")
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(c.Ctx.Request.Body)
|
||||
var p createPackageOrderPayload
|
||||
if err := json.Unmarshal(body, &p); err != nil {
|
||||
c.jsonErr(400, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
p.PackageCode = strings.TrimSpace(p.PackageCode)
|
||||
p.Channel = strings.TrimSpace(p.Channel)
|
||||
if p.PackageCode == "" {
|
||||
c.jsonErr(400, 400, "套餐不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var pkg models.SystemTenantPackage
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
||||
Filter("code", p.PackageCode).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("status", 1).
|
||||
One(&pkg); err != nil {
|
||||
c.jsonErr(404, 404, "套餐不存在或已下架")
|
||||
return
|
||||
}
|
||||
if pkg.Price <= 0 {
|
||||
c.jsonErr(400, 400, "该套餐未配置价格,请联系平台")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
order := &models.PlatformProductOrder{
|
||||
OrderNo: "PO" + strconv.FormatInt(now.UnixNano(), 10),
|
||||
Tid: tid,
|
||||
TenantName: c.tenantName(tid),
|
||||
ProductID: 0, // 套餐购买不关联产品
|
||||
ProductCode: pkg.Code,
|
||||
ProductName: pkg.Name,
|
||||
Amount: pkg.Price,
|
||||
PackageID: pkg.ID,
|
||||
Status: models.ProductOrderStatusPending,
|
||||
UpdateTime: &now,
|
||||
}
|
||||
|
||||
// 未选择支付渠道:仅落单,等待平台确认(线下转账/试用审批场景)
|
||||
if p.Channel == "" {
|
||||
if _, err := models.Orm.Insert(order); err != nil {
|
||||
c.jsonErr(500, 500, "下单失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
c.jsonOK(map[string]interface{}{
|
||||
"order_no": order.OrderNo,
|
||||
"pay_no": "",
|
||||
"amount": order.Amount,
|
||||
"offline": true,
|
||||
}, "下单成功,请等待平台开通")
|
||||
return
|
||||
}
|
||||
|
||||
amountFen := int64(order.Amount*100 + 0.5)
|
||||
subject := pkg.Name
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
payOrder, params, err := payment.CreatePayment(ctx, payment.CreateInput{
|
||||
OutTradeNo: order.OrderNo,
|
||||
OrderType: models.CommissionOrderTypeModuleShop,
|
||||
TenantID: strconv.FormatUint(tid, 10),
|
||||
TenantName: order.TenantName,
|
||||
Amount: amountFen,
|
||||
Channel: p.Channel,
|
||||
Subject: subject,
|
||||
ReturnURL: strings.TrimSpace(p.ReturnURL),
|
||||
ClientIP: c.Ctx.Input.IP(),
|
||||
PayType: p.PayType,
|
||||
})
|
||||
if err != nil {
|
||||
c.jsonErr(400, 400, "创建支付单失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
order.PayNo = payOrder.PayNo
|
||||
order.Channel = p.Channel
|
||||
if _, err := models.Orm.Insert(order); err != nil {
|
||||
c.jsonErr(500, 500, "下单失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
c.jsonOK(map[string]interface{}{
|
||||
"order_no": order.OrderNo,
|
||||
"pay_no": payOrder.PayNo,
|
||||
"amount": order.Amount,
|
||||
"channel": p.Channel,
|
||||
"pay_params": params,
|
||||
"offline": false,
|
||||
}, "下单成功")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user