Files

250 lines
6.8 KiB
Go

package admin
import (
"context"
"github.com/qianfree/team-api/internal/dao"
do "github.com/qianfree/team-api/internal/model/do"
"github.com/gogf/gf/v2/os/gtime"
v1 "github.com/qianfree/team-api/api/admin/v1"
"github.com/qianfree/team-api/internal/logic/common"
"github.com/qianfree/team-api/internal/utility/export"
)
// ListPlans 获取套餐列表
func (s *sAdmin) ListPlans(ctx context.Context, req *v1.PlanListReq) (*v1.PlanListRes, error) {
page, pageSize := common.NormalizePagination(req.Page, req.PageSize)
query := dao.PlnPlans.Ctx(ctx)
if req.Status != "" {
query = query.Where("status", req.Status)
}
var total int
plans := make([]*v1.PlanItem, 0)
err := query.OrderAsc("sort_order").
Page(page, pageSize).
ScanAndCount(&plans, &total, false)
if err != nil {
return nil, err
}
return &v1.PlanListRes{
Total: total,
Page: page,
PageSize: pageSize,
List: plans,
}, nil
}
// GetPlan 获取套餐详情
func (s *sAdmin) GetPlan(ctx context.Context, req *v1.PlanDetailReq) (*v1.PlanDetailRes, error) {
var plan *v1.PlanItem
err := dao.PlnPlans.Ctx(ctx).
Where("id", req.Id).
Scan(&plan)
if err = common.IgnoreScanNoRows(err); err != nil {
return nil, err
}
if plan == nil {
return nil, common.NewNotFoundError("套餐")
}
return &v1.PlanDetailRes{Data: plan}, nil
}
// CreatePlan 创建套餐
func (s *sAdmin) CreatePlan(ctx context.Context, req *v1.PlanCreateReq) (*v1.PlanCreateRes, error) {
result, err := dao.PlnPlans.Ctx(ctx).Insert(do.PlnPlans{
Name: req.Name,
Identifier: req.Identifier,
Description: req.Description,
MonthlyPrice: req.MonthlyPrice,
YearlyPrice: req.YearlyPrice,
MonthlyQuotaTokens: req.MonthlyQuotaTokens,
AllowedModels: req.AllowedModels,
IsRecommended: req.IsRecommended,
SortOrder: req.SortOrder,
})
if err != nil {
return nil, err
}
id, err := result.LastInsertId()
if err != nil {
return nil, err
}
return &v1.PlanCreateRes{ID: id}, nil
}
// UpdatePlan 更新套餐
func (s *sAdmin) UpdatePlan(ctx context.Context, req *v1.PlanUpdateReq) (*v1.PlanUpdateRes, error) {
data := do.PlnPlans{}
allowedFields := map[string]bool{
"name": true, "identifier": true, "description": true,
"monthly_price": true, "yearly_price": true,
"monthly_quota_tokens": true, "allowed_models": true,
"is_recommended": true, "sort_order": true, "status": true,
}
for k, v := range req.Update {
if !allowedFields[k] {
continue
}
switch k {
case "name":
data.Name = v.(string)
case "identifier":
data.Identifier = v.(string)
case "description":
data.Description = v.(string)
case "monthly_price":
data.MonthlyPrice = v
case "yearly_price":
data.YearlyPrice = v
case "monthly_quota_tokens":
data.MonthlyQuotaTokens = v
case "allowed_models":
if models, ok := v.([]string); ok {
data.AllowedModels = models
} else if models, ok := v.([]interface{}); ok {
result := make([]string, len(models))
for i, m := range models {
result[i] = m.(string)
}
data.AllowedModels = result
}
case "is_recommended":
data.IsRecommended = v
case "sort_order":
data.SortOrder = v
case "status":
data.Status = v.(string)
}
}
result, err := dao.PlnPlans.Ctx(ctx).
Where("id", req.Id).
Data(data).
Update()
if err != nil {
return nil, err
}
rows, err := result.RowsAffected()
if err != nil {
return nil, err
}
if rows == 0 {
return nil, common.NewNotFoundError("套餐")
}
return &v1.PlanUpdateRes{}, nil
}
// ArchivePlan 下架套餐(软删除)
func (s *sAdmin) ArchivePlan(ctx context.Context, req *v1.PlanArchiveReq) (*v1.PlanArchiveRes, error) {
result, err := dao.PlnPlans.Ctx(ctx).
Where("id", req.Id).
Where("status", "active").
Data(do.PlnPlans{
Status: "archived",
}).Update()
if err != nil {
return nil, err
}
rows, _ := result.RowsAffected()
if rows == 0 {
return nil, common.NewNotFoundError("plan")
}
return &v1.PlanArchiveRes{}, nil
}
// ToggleRecommend 切换推荐标记
func (s *sAdmin) ToggleRecommend(ctx context.Context, req *v1.PlanToggleRecommendReq) (*v1.PlanToggleRecommendRes, error) {
val, err := dao.PlnPlans.Ctx(ctx).
Where("id", req.Id).
Fields("is_recommended").
Value()
if err != nil {
return nil, err
}
isRecommended := val.Bool()
_, err = dao.PlnPlans.Ctx(ctx).
Where("id", req.Id).
Data(do.PlnPlans{
IsRecommended: !isRecommended,
}).Update()
if err != nil {
return nil, err
}
return &v1.PlanToggleRecommendRes{}, nil
}
// ExportPlans exports plan list to CSV or Excel.
func (s *sAdmin) ExportPlans(ctx context.Context, req *v1.PlanExportReq) (*v1.PlanExportRes, error) {
columns := []export.Column{
{Field: "id", Header: "ID"},
{Field: "name", Header: "名称"},
{Field: "identifier", Header: "标识"},
{Field: "monthly_price", Header: "月价"},
{Field: "yearly_price", Header: "年价"},
{Field: "status", Header: "状态"},
{Field: "monthly_quota_tokens", Header: "月配额Token"},
{Field: "is_recommended", Header: "推荐标记"},
{Field: "created_at", Header: "创建时间"},
}
config := export.Config{
Format: req.Format,
Filename: "套餐_" + gtime.Now().Format("Ymd_His"),
Columns: columns,
}
planFields := "id, name, identifier, monthly_price, yearly_price, status, monthly_quota_tokens, is_recommended, created_at"
return nil, export.GenericExport(ctx, config, func(yield func(map[string]any) bool) {
offset := 0
for {
query := dao.PlnPlans.Ctx(ctx)
if req.Status != "" {
query = query.Where("status", req.Status)
}
var batch []struct {
Id int64 `json:"id"`
Name string `json:"name"`
Identifier string `json:"identifier"`
MonthlyPrice float64 `json:"monthly_price"`
YearlyPrice float64 `json:"yearly_price"`
Status string `json:"status"`
MonthlyQuotaTokens int64 `json:"monthly_quota_tokens"`
IsRecommended bool `json:"is_recommended"`
CreatedAt *gtime.Time `json:"created_at"`
}
if err := query.Fields(planFields).OrderAsc("sort_order").Limit(1000).Offset(offset).Scan(&batch); err != nil {
return
}
for _, p := range batch {
isRec := "否"
if p.IsRecommended {
isRec = "是"
}
if !yield(map[string]any{
"id": p.Id,
"name": p.Name,
"identifier": p.Identifier,
"monthly_price": p.MonthlyPrice,
"yearly_price": p.YearlyPrice,
"status": p.Status,
"monthly_quota_tokens": p.MonthlyQuotaTokens,
"is_recommended": isRec,
"created_at": p.CreatedAt.String(),
}) {
return
}
}
if len(batch) < 1000 {
break
}
offset += 1000
}
})
}