f
This commit is contained in:
@@ -144,3 +144,52 @@ type ProductAdminListResponse struct {
|
||||
Size int `json:"size" comment:"每页数量"`
|
||||
Items []ProductAdminInfoResponse `json:"items" comment:"产品列表"`
|
||||
}
|
||||
|
||||
// PackageItemAIResponse AI组合包项目响应(不含成本价)
|
||||
type PackageItemAIResponse struct {
|
||||
ID string `json:"id" comment:"项目ID"`
|
||||
ProductID string `json:"product_id" comment:"子产品ID"`
|
||||
ProductCode string `json:"product_code" comment:"子产品编号"`
|
||||
ProductName string `json:"product_name" comment:"子产品名称"`
|
||||
SortOrder int `json:"sort_order" comment:"排序"`
|
||||
Price float64 `json:"price" comment:"子产品价格"`
|
||||
}
|
||||
|
||||
// ProductAIInfoResponse AI产品详情响应(含启用/可见状态,不含用户状态、成本价、备注)
|
||||
type ProductAIInfoResponse struct {
|
||||
ID string `json:"id" comment:"产品ID"`
|
||||
OldID *string `json:"old_id,omitempty" comment:"旧产品ID"`
|
||||
Name string `json:"name" comment:"产品名称"`
|
||||
Code string `json:"code" comment:"产品编号"`
|
||||
Description string `json:"description" comment:"产品简介"`
|
||||
Content string `json:"content" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" comment:"一级分类ID"`
|
||||
SubCategoryID *string `json:"sub_category_id,omitempty" comment:"二级分类ID"`
|
||||
Price float64 `json:"price" comment:"产品价格"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否可见"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
|
||||
SellUIComponent bool `json:"sell_ui_component" comment:"是否出售UI组件"`
|
||||
UIComponentPrice float64 `json:"ui_component_price" comment:"UI组件销售价格(组合包使用)"`
|
||||
|
||||
SEOTitle string `json:"seo_title" comment:"SEO标题"`
|
||||
SEODescription string `json:"seo_description" comment:"SEO描述"`
|
||||
SEOKeywords string `json:"seo_keywords" comment:"SEO关键词"`
|
||||
|
||||
Category *CategoryInfoResponse `json:"category,omitempty" comment:"一级分类信息"`
|
||||
SubCategory *SubCategoryInfoResponse `json:"sub_category,omitempty" comment:"二级分类信息"`
|
||||
|
||||
PackageItems []*PackageItemAIResponse `json:"package_items,omitempty" comment:"组合包项目列表"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" comment:"更新时间"`
|
||||
}
|
||||
|
||||
// ProductAIListResponse AI产品列表响应
|
||||
type ProductAIListResponse struct {
|
||||
Total int64 `json:"total" comment:"总数"`
|
||||
Page int `json:"page" comment:"页码"`
|
||||
Size int `json:"size" comment:"每页数量"`
|
||||
Items []ProductAIInfoResponse `json:"items" comment:"产品列表"`
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ type ProductApplicationService interface {
|
||||
ListProductsForAdmin(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductAdminListResponse, error)
|
||||
GetProductByIDForAdmin(ctx context.Context, query *queries.GetProductDetailQuery) (*responses.ProductAdminInfoResponse, error)
|
||||
|
||||
// AI专用方法
|
||||
ListProductsForAI(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductAIListResponse, error)
|
||||
|
||||
// 用户端专用方法
|
||||
GetProductByIDForUser(ctx context.Context, query *queries.GetProductDetailQuery) (*responses.ProductInfoWithDocumentResponse, error)
|
||||
|
||||
|
||||
@@ -415,6 +415,27 @@ func (s *ProductApplicationServiceImpl) GetAvailableProducts(ctx context.Context
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListProductsForAI 获取产品列表(AI专用)
|
||||
// 业务流程:1. 获取全部产品(含禁用/隐藏) 2. 构建AI响应数据(无用户状态、成本价、备注)
|
||||
func (s *ProductApplicationServiceImpl) ListProductsForAI(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductAIListResponse, error) {
|
||||
products, total, err := s.productManagementService.ListProducts(ctx, filters, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]responses.ProductAIInfoResponse, len(products))
|
||||
for i := range products {
|
||||
items[i] = *s.convertToProductAIInfoResponse(products[i])
|
||||
}
|
||||
|
||||
return &responses.ProductAIListResponse{
|
||||
Total: total,
|
||||
Page: options.Page,
|
||||
Size: options.PageSize,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListProductsForAdmin 获取产品列表(管理员专用)
|
||||
// 业务流程:1. 获取所有产品列表(包括隐藏的) 2. 构建管理员响应数据
|
||||
func (s *ProductApplicationServiceImpl) ListProductsForAdmin(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductAdminListResponse, error) {
|
||||
@@ -546,6 +567,55 @@ func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *en
|
||||
return response
|
||||
}
|
||||
|
||||
// convertToProductAIInfoResponse 转换为AI产品信息响应
|
||||
func (s *ProductApplicationServiceImpl) convertToProductAIInfoResponse(product *entities.Product) *responses.ProductAIInfoResponse {
|
||||
response := &responses.ProductAIInfoResponse{
|
||||
ID: product.ID,
|
||||
OldID: product.OldID,
|
||||
Name: product.Name,
|
||||
Code: product.Code,
|
||||
Description: product.Description,
|
||||
Content: product.Content,
|
||||
CategoryID: product.CategoryID,
|
||||
SubCategoryID: product.SubCategoryID,
|
||||
Price: product.Price.InexactFloat64(),
|
||||
IsEnabled: product.IsEnabled,
|
||||
IsVisible: product.IsVisible,
|
||||
IsPackage: product.IsPackage,
|
||||
SellUIComponent: product.SellUIComponent,
|
||||
UIComponentPrice: product.UIComponentPrice.InexactFloat64(),
|
||||
SEOTitle: product.SEOTitle,
|
||||
SEODescription: product.SEODescription,
|
||||
SEOKeywords: product.SEOKeywords,
|
||||
CreatedAt: product.CreatedAt,
|
||||
UpdatedAt: product.UpdatedAt,
|
||||
}
|
||||
|
||||
if product.Category != nil {
|
||||
response.Category = s.convertToCategoryInfoResponse(product.Category)
|
||||
}
|
||||
|
||||
if product.SubCategory != nil {
|
||||
response.SubCategory = s.convertToSubCategoryInfoResponse(product.SubCategory)
|
||||
}
|
||||
|
||||
if product.IsPackage && len(product.PackageItems) > 0 {
|
||||
response.PackageItems = make([]*responses.PackageItemAIResponse, len(product.PackageItems))
|
||||
for i, item := range product.PackageItems {
|
||||
response.PackageItems[i] = &responses.PackageItemAIResponse{
|
||||
ID: item.ID,
|
||||
ProductID: item.ProductID,
|
||||
ProductCode: item.Product.Code,
|
||||
ProductName: item.Product.Name,
|
||||
SortOrder: item.SortOrder,
|
||||
Price: item.Product.Price.InexactFloat64(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// convertToProductAdminInfoResponse 转换为管理员产品信息响应
|
||||
func (s *ProductApplicationServiceImpl) convertToProductAdminInfoResponse(product *entities.Product) *responses.ProductAdminInfoResponse {
|
||||
response := &responses.ProductAdminInfoResponse{
|
||||
|
||||
@@ -1430,6 +1430,8 @@ func NewContainer() *Container {
|
||||
routes.NewPDFGRoutes,
|
||||
// 企业报告页面路由
|
||||
routes.NewQYGLReportRoutes,
|
||||
// AI产品路由
|
||||
routes.NewAIProductRoutes,
|
||||
),
|
||||
|
||||
// 应用生命周期
|
||||
@@ -1550,6 +1552,7 @@ func RegisterRoutes(
|
||||
publicQueryWhitelistRoutes *routes.PublicQueryWhitelistRoutes,
|
||||
pdfgRoutes *routes.PDFGRoutes,
|
||||
qyglReportRoutes *routes.QYGLReportRoutes,
|
||||
aiProductRoutes *routes.AIProductRoutes,
|
||||
jwtAuth *middleware.JWTAuthMiddleware,
|
||||
adminAuth *middleware.AdminAuthMiddleware,
|
||||
cfg *config.Config,
|
||||
@@ -1580,6 +1583,7 @@ func RegisterRoutes(
|
||||
publicQueryWhitelistRoutes.Register(router)
|
||||
pdfgRoutes.Register(router)
|
||||
qyglReportRoutes.Register(router)
|
||||
aiProductRoutes.Register(router)
|
||||
|
||||
// 打印注册的路由信息
|
||||
router.PrintRoutes()
|
||||
|
||||
@@ -166,6 +166,80 @@ func (h *ProductHandler) ListProducts(c *gin.Context) {
|
||||
h.responseBuilder.Success(c, result, "获取产品列表成功")
|
||||
}
|
||||
|
||||
// ListProductsForAI 获取产品列表(AI专用)
|
||||
// @Summary 获取产品列表(AI专用)
|
||||
// @Description AI调用专用产品列表,返回全部产品(含禁用/隐藏),包含 is_enabled/is_visible,不含用户订阅状态、成本价和备注
|
||||
// @Tags AI接口
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码" default(1)
|
||||
// @Param page_size query int false "每页数量" default(10)
|
||||
// @Param keyword query string false "搜索关键词"
|
||||
// @Param category_id query string false "分类ID"
|
||||
// @Param is_enabled query bool false "是否启用"
|
||||
// @Param is_visible query bool false "是否可见"
|
||||
// @Param is_package query bool false "是否组合包"
|
||||
// @Param sort_by query string false "排序字段"
|
||||
// @Param sort_order query string false "排序方向" Enums(asc, desc)
|
||||
// @Success 200 {object} responses.ProductAIListResponse "获取产品列表成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/ai/products [get]
|
||||
func (h *ProductHandler) ListProductsForAI(c *gin.Context) {
|
||||
page := h.getIntQuery(c, "page", 1)
|
||||
pageSize := h.getIntQuery(c, "page_size", 10)
|
||||
|
||||
filters := make(map[string]interface{})
|
||||
|
||||
if keyword := c.Query("keyword"); keyword != "" {
|
||||
filters["keyword"] = keyword
|
||||
}
|
||||
if categoryID := c.Query("category_id"); categoryID != "" {
|
||||
filters["category_id"] = categoryID
|
||||
}
|
||||
if isEnabled := c.Query("is_enabled"); isEnabled != "" {
|
||||
if enabled, err := strconv.ParseBool(isEnabled); err == nil {
|
||||
filters["is_enabled"] = enabled
|
||||
}
|
||||
}
|
||||
if isVisible := c.Query("is_visible"); isVisible != "" {
|
||||
if visible, err := strconv.ParseBool(isVisible); err == nil {
|
||||
filters["is_visible"] = visible
|
||||
}
|
||||
}
|
||||
if isPackage := c.Query("is_package"); isPackage != "" {
|
||||
if pkg, err := strconv.ParseBool(isPackage); err == nil {
|
||||
filters["is_package"] = pkg
|
||||
}
|
||||
}
|
||||
|
||||
sortBy := c.Query("sort_by")
|
||||
if sortBy == "" {
|
||||
sortBy = "created_at"
|
||||
}
|
||||
|
||||
sortOrder := c.Query("sort_order")
|
||||
if sortOrder == "" {
|
||||
sortOrder = "desc"
|
||||
}
|
||||
|
||||
options := interfaces.ListOptions{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Sort: sortBy,
|
||||
Order: sortOrder,
|
||||
}
|
||||
|
||||
result, err := h.appService.ListProductsForAI(c.Request.Context(), filters, options)
|
||||
if err != nil {
|
||||
h.logger.Error("获取AI产品列表失败", zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "获取产品列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, result, "获取产品列表成功")
|
||||
}
|
||||
|
||||
// getIntQuery 获取整数查询参数
|
||||
func (h *ProductHandler) getIntQuery(c *gin.Context, key string, defaultValue int) int {
|
||||
if value := c.Query(key); value != "" {
|
||||
|
||||
37
internal/infrastructure/http/routes/ai_product_routes.go
Normal file
37
internal/infrastructure/http/routes/ai_product_routes.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"tyapi-server/internal/infrastructure/http/handlers"
|
||||
sharedhttp "tyapi-server/internal/shared/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// AIProductRoutes AI产品接口路由
|
||||
type AIProductRoutes struct {
|
||||
productHandler *handlers.ProductHandler
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAIProductRoutes 创建AI产品路由
|
||||
func NewAIProductRoutes(
|
||||
productHandler *handlers.ProductHandler,
|
||||
logger *zap.Logger,
|
||||
) *AIProductRoutes {
|
||||
return &AIProductRoutes{
|
||||
productHandler: productHandler,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Register 注册AI产品相关路由
|
||||
func (r *AIProductRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
engine := router.GetEngine()
|
||||
|
||||
aiProducts := engine.Group("/api/v1/ai/products")
|
||||
{
|
||||
aiProducts.GET("", r.productHandler.ListProductsForAI)
|
||||
}
|
||||
|
||||
r.logger.Info("AI产品路由注册完成")
|
||||
}
|
||||
Reference in New Issue
Block a user