package controller import ( "errors" "fmt" "net/http" "strconv" "strings" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/service" "github.com/gin-gonic/gin" ) // SupplierApplicationSubmitRequest 供应商提交申请请求体。 type SupplierApplicationSubmitRequest struct { ApplicantUserID int `json:"applicant_user_id"` SupplierAlias string `json:"supplier_alias"` SupplierType string `json:"supplier_type"` CompanyName string `json:"company_name"` CreditCode string `json:"credit_code"` BusinessLicenseURL string `json:"business_license_url"` BusinessLicenseFile string `json:"business_license_file"` CompanyLogoURL string `json:"company_logo_url"` LegalRepresentative string `json:"legal_representative"` CompanySize string `json:"company_size"` ContactName string `json:"contact_name"` ContactMobile string `json:"contact_mobile"` ContactWechat string `json:"contact_wechat"` } // getSupplierApplicationMissingFieldMessage 返回供应商申请缺失字段的中文提示。 func getSupplierApplicationMissingFieldMessage(req SupplierApplicationSubmitRequest) string { switch { case req.CompanyName == "": return "请填写企业/主体名称(company_name)" case req.CreditCode == "": return "请填写统一社会信用代码(credit_code)" case req.BusinessLicenseURL == "": return "请上传营业执照(business_license_url)" case req.CompanyLogoURL == "": return "请上传企业Logo(company_logo_url)" case req.LegalRepresentative == "": return "请填写法人/经营者姓名(legal_representative)" case req.ContactName == "": return "请填写对接人姓名(contact_name)" case req.ContactMobile == "": return "请填写对接人手机号(contact_mobile)" case req.ContactWechat == "": return "请填写对接人微信/企业微信(contact_wechat)" default: return "" } } // SupplierApplicationReviewRequest 管理员审核请求体。 type SupplierApplicationReviewRequest struct { Status int `json:"status"` Reason string `json:"reason"` SupplierAlias string `json:"supplier_alias"` SupplierType string `json:"supplier_type"` } var allowedSupplierTypes = map[string]struct{}{ "公有云": {}, "AIDC": {}, "企业中转站": {}, "个人中转站": {}, } // isValidSupplierType 判断供应商类型是否在允许枚举内。 func isValidSupplierType(supplierType string) bool { _, ok := allowedSupplierTypes[supplierType] return ok } // SupplierDeactivateRequest 供应商注销请求体。 type SupplierDeactivateRequest struct { SupplierID int `json:"supplier_id"` Reason string `json:"reason"` } // PublishUserMessageRequest 管理员发布站内消息请求体。 type PublishUserMessageRequest struct { ReceiverUserID int `json:"receiver_user_id"` ReceiverMinRole int `json:"receiver_min_role"` Type string `json:"type"` Title string `json:"title"` Content string `json:"content"` BizType string `json:"biz_type"` BizID int `json:"biz_id"` } // SupplierApplicationUpdateRequest 供应商修改申请请求体(必须带申请ID)。 type SupplierApplicationUpdateRequest struct { ID int `json:"id"` SupplierApplicationSubmitRequest } // SupplierCapabilityUpsertRequest 供应商技术能力档案写入请求体。 type SupplierCapabilityUpsertRequest struct { CoreServiceTypes []string `json:"core_service_types"` SupportedModels []string `json:"supported_models"` SupportedModelNotes string `json:"supported_model_notes"` SupportedAPIEndpoints []string `json:"supported_api_endpoints"` SupportedAPIEndpointExtra string `json:"supported_api_endpoint_extra"` SupportedParams []string `json:"supported_params"` SupportedParamsExtra string `json:"supported_params_extra"` StreamingSupported bool `json:"streaming_supported"` StreamingNotes string `json:"streaming_notes"` StructuredOutputSupported bool `json:"structured_output_supported"` StructuredOutputNotes string `json:"structured_output_notes"` MultimodalTypes []string `json:"multimodal_types"` MultimodalExtra string `json:"multimodal_extra"` PricingModes []string `json:"pricing_modes"` ReferenceInputPrice string `json:"reference_input_price"` ReferenceOutputPrice string `json:"reference_output_price"` FailureBillingMode string `json:"failure_billing_mode"` FailureBillingNotes string `json:"failure_billing_notes"` APIBaseURLs []string `json:"api_base_urls"` OpenAICompatible bool `json:"openai_compatible"` TruthCommitmentConfirmed bool `json:"truth_commitment_confirmed"` } // requireApprovedSupplierApplication 要求当前用户为审核通过供应商。 func requireApprovedSupplierApplication(c *gin.Context) (*model.SupplierApplication, bool) { app, err := model.GetApprovedSupplierApplicationByApplicant(c.GetInt("id")) if err != nil { if model.IsSupplierApplicationNotFound(err) { c.JSON(http.StatusForbidden, gin.H{"success": false, "message": "当前用户未通过供应商审核,无法操作渠道或模型"}) return nil, false } common.ApiError(c, err) return nil, false } return app, true } // readOptionalStatusQuery 读取可选 status 查询参数。 func readOptionalStatusQuery(c *gin.Context) (*int, error) { statusRaw := strings.TrimSpace(c.Query("status")) if statusRaw == "" { return nil, nil } status, err := strconv.Atoi(statusRaw) if err != nil { return nil, err } if status < model.SupplierApplicationStatusPending || status > model.SupplierApplicationStatusDeactivated { return nil, errors.New("invalid status") } return &status, nil } // readSupplierStatusListQuery 读取供应商列表状态查询参数(支持逗号分隔)。 // 未传时默认返回“审核通过 + 已注销”。 func readSupplierStatusListQuery(c *gin.Context) ([]int, error) { statusRaw := strings.TrimSpace(c.Query("status")) if statusRaw == "" { return []int{model.SupplierApplicationStatusApproved, model.SupplierApplicationStatusDeactivated}, nil } statusParts := strings.Split(statusRaw, ",") statuses := make([]int, 0, len(statusParts)) seen := make(map[int]struct{}, len(statusParts)) for _, part := range statusParts { part = strings.TrimSpace(part) if part == "" { continue } status, err := strconv.Atoi(part) if err != nil { return nil, err } if status < model.SupplierApplicationStatusPending || status > model.SupplierApplicationStatusDeactivated { return nil, errors.New("invalid status") } if _, ok := seen[status]; ok { continue } seen[status] = struct{}{} statuses = append(statuses, status) } if len(statuses) == 0 { return nil, errors.New("empty status") } return statuses, nil } // trimAndFilterStrings 清理字符串数组中的空白项。 func trimAndFilterStrings(values []string) []string { cleaned := make([]string, 0, len(values)) for _, item := range values { trimmed := strings.TrimSpace(item) if trimmed == "" { continue } cleaned = append(cleaned, trimmed) } return cleaned } // marshalStringArray 将字符串数组编码为 JSON 字符串。 func marshalStringArray(values []string) (string, error) { bytes, err := common.Marshal(trimAndFilterStrings(values)) if err != nil { return "", err } return string(bytes), nil } // unmarshalStringArray 将 JSON 字符串解码为字符串数组。 func unmarshalStringArray(raw string) []string { raw = strings.TrimSpace(raw) if raw == "" { return []string{} } var items []string if err := common.UnmarshalJsonStr(raw, &items); err != nil { return []string{} } return trimAndFilterStrings(items) } // buildSupplierCapabilityModel 将请求体转换为模型对象。 func buildSupplierCapabilityModel(req *SupplierCapabilityUpsertRequest) (*model.SupplierCapability, error) { coreServiceTypes, err := marshalStringArray(req.CoreServiceTypes) if err != nil { return nil, err } supportedModels, err := marshalStringArray(req.SupportedModels) if err != nil { return nil, err } supportedAPIEndpoints, err := marshalStringArray(req.SupportedAPIEndpoints) if err != nil { return nil, err } supportedParams, err := marshalStringArray(req.SupportedParams) if err != nil { return nil, err } multimodalTypes, err := marshalStringArray(req.MultimodalTypes) if err != nil { return nil, err } pricingModes, err := marshalStringArray(req.PricingModes) if err != nil { return nil, err } apiBaseURLs, err := marshalStringArray(req.APIBaseURLs) if err != nil { return nil, err } return &model.SupplierCapability{ CoreServiceTypes: coreServiceTypes, SupportedModels: supportedModels, SupportedModelNotes: strings.TrimSpace(req.SupportedModelNotes), SupportedAPIEndpoints: supportedAPIEndpoints, SupportedAPIEndpointExtra: strings.TrimSpace(req.SupportedAPIEndpointExtra), SupportedParams: supportedParams, SupportedParamsExtra: strings.TrimSpace(req.SupportedParamsExtra), StreamingSupported: req.StreamingSupported, StreamingNotes: strings.TrimSpace(req.StreamingNotes), StructuredOutputSupported: req.StructuredOutputSupported, StructuredOutputNotes: strings.TrimSpace(req.StructuredOutputNotes), MultimodalTypes: multimodalTypes, MultimodalExtra: strings.TrimSpace(req.MultimodalExtra), PricingModes: pricingModes, ReferenceInputPrice: strings.TrimSpace(req.ReferenceInputPrice), ReferenceOutputPrice: strings.TrimSpace(req.ReferenceOutputPrice), FailureBillingMode: strings.TrimSpace(req.FailureBillingMode), FailureBillingNotes: strings.TrimSpace(req.FailureBillingNotes), APIBaseURLs: apiBaseURLs, OpenAICompatible: req.OpenAICompatible, TruthCommitmentConfirmed: req.TruthCommitmentConfirmed, }, nil } // buildSupplierCapabilityResponse 将模型对象转换为接口响应对象。 func buildSupplierCapabilityResponse(capability *model.SupplierCapability) gin.H { if capability == nil { return nil } return gin.H{ "id": capability.ID, "supplier_application_id": capability.SupplierApplicationID, "core_service_types": unmarshalStringArray(capability.CoreServiceTypes), "supported_models": unmarshalStringArray(capability.SupportedModels), "supported_model_notes": capability.SupportedModelNotes, "supported_api_endpoints": unmarshalStringArray(capability.SupportedAPIEndpoints), "supported_api_endpoint_extra": capability.SupportedAPIEndpointExtra, "supported_params": unmarshalStringArray(capability.SupportedParams), "supported_params_extra": capability.SupportedParamsExtra, "streaming_supported": capability.StreamingSupported, "streaming_notes": capability.StreamingNotes, "structured_output_supported": capability.StructuredOutputSupported, "structured_output_notes": capability.StructuredOutputNotes, "multimodal_types": unmarshalStringArray(capability.MultimodalTypes), "multimodal_extra": capability.MultimodalExtra, "pricing_modes": unmarshalStringArray(capability.PricingModes), "reference_input_price": capability.ReferenceInputPrice, "reference_output_price": capability.ReferenceOutputPrice, "failure_billing_mode": capability.FailureBillingMode, "failure_billing_notes": capability.FailureBillingNotes, "api_base_urls": unmarshalStringArray(capability.APIBaseURLs), "openai_compatible": capability.OpenAICompatible, "truth_commitment_confirmed": capability.TruthCommitmentConfirmed, "created_at": capability.CreatedAt, "updated_at": capability.UpdatedAt, } } // attachSupplierCapability 为供应商申请对象补充技术能力档案。 func attachSupplierCapability(app *model.SupplierApplication) error { if app == nil { return nil } capability, err := model.GetSupplierCapabilityByApplicationID(app.ID) if err != nil { if model.IsSupplierCapabilityNotFound(err) { app.SupplierCapability = nil return nil } return err } app.SupplierCapability = capability return nil } // SubmitSupplierApplication godoc // @Summary 提交供应商入驻申请 // @Description 普通用户提交供应商申请,提交后生成管理员待审核站内消息 // @Tags Supplier // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param request body SupplierApplicationSubmitRequest true "申请信息" // @Success 200 {object} map[string]interface{} "success + data{id,status}" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/supplier/application [post] func SubmitSupplierApplication(c *gin.Context) { var req SupplierApplicationSubmitRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的参数"}) return } req.CompanyName = strings.TrimSpace(req.CompanyName) req.CreditCode = strings.TrimSpace(req.CreditCode) req.BusinessLicenseURL = strings.TrimSpace(req.BusinessLicenseURL) req.BusinessLicenseFile = strings.TrimSpace(req.BusinessLicenseFile) req.CompanyLogoURL = strings.TrimSpace(req.CompanyLogoURL) req.SupplierType = strings.TrimSpace(req.SupplierType) req.LegalRepresentative = strings.TrimSpace(req.LegalRepresentative) req.CompanySize = strings.TrimSpace(req.CompanySize) req.ContactName = strings.TrimSpace(req.ContactName) req.ContactMobile = strings.TrimSpace(req.ContactMobile) req.ContactWechat = strings.TrimSpace(req.ContactWechat) if missingFieldMessage := getSupplierApplicationMissingFieldMessage(req); missingFieldMessage != "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": missingFieldMessage}) return } if len(req.CreditCode) != 18 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "统一社会信用代码需为18位"}) return } isAdminOrAbove := c.GetInt("role") >= common.RoleAdminUser applicantUserID := c.GetInt("id") if isAdminOrAbove { if req.SupplierType == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "管理员代添加供应商时必须选择供应商类型"}) return } if !isValidSupplierType(req.SupplierType) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的供应商类型"}) return } if req.ApplicantUserID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "管理员代添加供应商时必须提供有效的applicant_user_id"}) return } if _, err := model.GetUserById(req.ApplicantUserID, false); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "指定的关联用户不存在"}) return } applicantUserID = req.ApplicantUserID } app := &model.SupplierApplication{ ApplicantUserID: applicantUserID, CompanyName: req.CompanyName, CreditCode: req.CreditCode, BusinessLicenseURL: req.BusinessLicenseURL, BusinessLicenseFile: req.BusinessLicenseFile, CompanyLogoURL: req.CompanyLogoURL, SupplierType: req.SupplierType, LegalRepresentative: req.LegalRepresentative, CompanySize: req.CompanySize, ContactName: req.ContactName, ContactMobile: req.ContactMobile, ContactWechat: req.ContactWechat, } var err error if isAdminOrAbove { err = model.CreateSupplierApplicationAutoApproved(app, c.GetInt("id")) } else { app.Status = model.SupplierApplicationStatusPending err = model.CreateSupplierApplication(app) } if err != nil { if model.IsSupplierCreditCodeDuplicateError(err) { common.ApiErrorMsg(c, "统一社会信用代码已存在,请核对后重试") return } common.ApiError(c, err) return } if !isAdminOrAbove { _ = model.CreateSupplierApplicationAudit(&model.SupplierApplicationAudit{ ApplicationID: app.ID, OperatorUserID: app.ApplicantUserID, Action: model.SupplierApplicationAuditActionSubmit, FromStatus: model.SupplierApplicationStatusPending, ToStatus: model.SupplierApplicationStatusPending, Reason: "", }) _ = service.PublishUserMessage(&model.UserMessage{ ReceiverUserID: 0, ReceiverMinRole: common.RoleAdminUser, Type: model.UserMessageTypeSupplierSubmitted, Title: "供应商入驻待审核", Content: fmt.Sprintf("收到新的供应商申请:%s(统一社会信用代码:%s)", app.CompanyName, app.CreditCode), BizType: model.UserMessageBizTypeSupplierApplication, BizID: app.ID, }) } common.ApiSuccess(c, gin.H{ "id": app.ID, "status": app.Status, }) } // GetMySupplierApplication godoc // @Summary 查询当前用户供应商申请 // @Tags Supplier // @Produce json // @Security CookieAuth // @Security ApiUserID // @Success 200 {object} map[string]interface{} "success + data{申请对象或null}" // @Router /user/supplier/application/self [get] func GetMySupplierApplication(c *gin.Context) { app, err := model.GetMySupplierApplication(c.GetInt("id")) if err != nil { if model.IsSupplierApplicationNotFound(err) { common.ApiSuccess(c, nil) return } common.ApiError(c, err) return } if err = attachSupplierCapability(app); err != nil { common.ApiError(c, err) return } response := gin.H{ "id": app.ID, "applicant_user_id": app.ApplicantUserID, "company_name": app.CompanyName, "credit_code": app.CreditCode, "business_license_url": app.BusinessLicenseURL, "business_license_file": app.BusinessLicenseFile, "company_logo_url": app.CompanyLogoURL, "supplier_type": app.SupplierType, "legal_representative": app.LegalRepresentative, "company_size": app.CompanySize, "contact_name": app.ContactName, "contact_mobile": app.ContactMobile, "contact_wechat": app.ContactWechat, "supplier_alias": app.SupplierAlias, "status": app.Status, "review_reason": app.ReviewReason, "reviewed_by": app.ReviewedBy, "reviewed_at": app.ReviewedAt, "created_at": app.CreatedAt, "updated_at": app.UpdatedAt, "supplier_capability": buildSupplierCapabilityResponse(app.SupplierCapability), } common.ApiSuccess(c, response) } // GetSupplierCapability godoc // @Summary 查询供应商技术能力档案 // @Description 管理员可查任意申请;普通用户仅可查询自己的申请 // @Tags Supplier // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param id path int true "供应商申请ID" // @Success 200 {object} map[string]interface{} "success + data{供应商技术能力档案}" // @Router /user/supplier/application/{id}/capability [get] func GetSupplierCapability(c *gin.Context) { applicationID, err := strconv.Atoi(c.Param("id")) if err != nil || applicationID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的供应商申请ID"}) return } app, err := model.GetSupplierByID(applicationID) if err != nil { if model.IsSupplierApplicationNotFound(err) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "未找到供应商申请"}) return } common.ApiError(c, err) return } if c.GetInt("role") < common.RoleAdminUser && app.ApplicantUserID != c.GetInt("id") { c.JSON(http.StatusForbidden, gin.H{"success": false, "message": "无权查看该供应商技术能力档案"}) return } capability, err := model.GetSupplierCapabilityByApplicationID(applicationID) if err != nil { if model.IsSupplierCapabilityNotFound(err) { common.ApiSuccess(c, nil) return } common.ApiError(c, err) return } common.ApiSuccess(c, buildSupplierCapabilityResponse(capability)) } // UpsertSupplierCapability godoc // @Summary 保存供应商技术能力档案 // @Description 管理员可保存任意申请;普通用户仅可保存自己的申请 // @Tags Supplier // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param id path int true "供应商申请ID" // @Param request body SupplierCapabilityUpsertRequest true "供应商技术能力档案" // @Success 200 {object} map[string]interface{} "success + data{供应商技术能力档案}" // @Router /user/supplier/application/{id}/capability [put] func UpsertSupplierCapability(c *gin.Context) { applicationID, err := strconv.Atoi(c.Param("id")) if err != nil || applicationID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的供应商申请ID"}) return } app, err := model.GetSupplierByID(applicationID) if err != nil { if model.IsSupplierApplicationNotFound(err) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "未找到供应商申请"}) return } common.ApiError(c, err) return } if c.GetInt("role") < common.RoleAdminUser && app.ApplicantUserID != c.GetInt("id") { c.JSON(http.StatusForbidden, gin.H{"success": false, "message": "无权修改该供应商技术能力档案"}) return } var req SupplierCapabilityUpsertRequest if err = c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的参数"}) return } capabilityModel, err := buildSupplierCapabilityModel(&req) if err != nil { common.ApiError(c, err) return } updated, err := model.UpsertSupplierCapabilityByApplicationID(applicationID, capabilityModel) if err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, buildSupplierCapabilityResponse(updated)) } // UpdateMySupplierApplication godoc // @Summary 修改当前用户供应商申请并重新提交 // @Description 当前申请只要未审核通过都可修改,修改后状态重置为待审核(0) // @Tags Supplier // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param request body SupplierApplicationUpdateRequest true "申请信息(含id)" // @Success 200 {object} map[string]interface{} "success + data{id,status}" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/supplier/application/self [put] func UpdateMySupplierApplication(c *gin.Context) { var req SupplierApplicationUpdateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的参数"}) return } if req.ID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "修改申请时必须提供有效的id"}) return } req.CompanyName = strings.TrimSpace(req.CompanyName) req.CreditCode = strings.TrimSpace(req.CreditCode) req.BusinessLicenseURL = strings.TrimSpace(req.BusinessLicenseURL) req.BusinessLicenseFile = strings.TrimSpace(req.BusinessLicenseFile) req.CompanyLogoURL = strings.TrimSpace(req.CompanyLogoURL) req.SupplierType = strings.TrimSpace(req.SupplierType) req.LegalRepresentative = strings.TrimSpace(req.LegalRepresentative) req.CompanySize = strings.TrimSpace(req.CompanySize) req.ContactName = strings.TrimSpace(req.ContactName) req.ContactMobile = strings.TrimSpace(req.ContactMobile) req.ContactWechat = strings.TrimSpace(req.ContactWechat) if req.CompanyName == "" || req.CreditCode == "" || req.BusinessLicenseURL == "" || req.CompanyLogoURL == "" || req.LegalRepresentative == "" || req.ContactName == "" || req.ContactMobile == "" || req.ContactWechat == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "请填写完整的必填字段"}) return } if len(req.CreditCode) != 18 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "统一社会信用代码需为18位"}) return } app, err := model.UpdateMySupplierApplication(c.GetInt("id"), req.ID, &model.SupplierApplication{ CompanyName: req.CompanyName, CreditCode: req.CreditCode, BusinessLicenseURL: req.BusinessLicenseURL, BusinessLicenseFile: req.BusinessLicenseFile, CompanyLogoURL: req.CompanyLogoURL, SupplierType: req.SupplierType, LegalRepresentative: req.LegalRepresentative, CompanySize: req.CompanySize, ContactName: req.ContactName, ContactMobile: req.ContactMobile, ContactWechat: req.ContactWechat, }) if err != nil { if model.IsSupplierCreditCodeDuplicateError(err) { common.ApiErrorMsg(c, "统一社会信用代码已存在,请核对后重试") return } if errors.Is(err, model.ErrSupplierApplicationStatusNotEditable) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "当前申请状态不可修改"}) return } if model.IsSupplierApplicationNotFound(err) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "未找到可修改的供应商申请"}) return } common.ApiError(c, err) return } _ = service.PublishUserMessage(&model.UserMessage{ ReceiverUserID: 0, ReceiverMinRole: common.RoleAdminUser, Type: model.UserMessageTypeSupplierSubmitted, Title: "供应商入驻待审核", Content: fmt.Sprintf("供应商申请已更新并重新提交:%s(统一社会信用代码:%s)", app.CompanyName, app.CreditCode), BizType: model.UserMessageBizTypeSupplierApplication, BizID: app.ID, }) common.ApiSuccess(c, gin.H{ "id": app.ID, "status": app.Status, }) } // AdminListSupplierApplications godoc // @Summary 管理员分页查询供应商申请 // @Tags SupplierAdmin // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param p query int false "页码" // @Param page_size query int false "每页数量" // @Param status query int false "状态:0待审核 1审核通过 2审核驳回" // @Success 200 {object} map[string]interface{} "分页结果" // @Router /user/supplier/application [get] func AdminListSupplierApplications(c *gin.Context) { status, err := readOptionalStatusQuery(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的status参数"}) return } pageInfo := common.GetPageQuery(c) items, total, err := model.ListSupplierApplications(status, pageInfo) if err != nil { common.ApiError(c, err) return } pageInfo.SetTotal(int(total)) pageInfo.SetItems(items) common.ApiSuccess(c, pageInfo) } // AdminListSuppliers godoc // @Summary 管理员分页查询供应商列表 // @Description 支持按供应商名称模糊查询,返回分页数据 // @Tags SupplierAdmin // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param p query int false "页码" // @Param page_size query int false "每页数量" // @Param company_name query string false "供应商名称(模糊)" // @Param status query string false "状态筛选,支持逗号分隔(如1,3);默认查询1和3" // @Success 200 {object} map[string]interface{} "分页结果" // @Router /user/supplier/list [get] func AdminListSuppliers(c *gin.Context) { pageInfo := common.GetPageQuery(c) companyName := strings.TrimSpace(c.Query("company_name")) statuses, err := readSupplierStatusListQuery(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的status参数"}) return } items, total, err := model.ListSuppliersByCompanyName(companyName, statuses, pageInfo) if err != nil { common.ApiError(c, err) return } pageInfo.SetTotal(int(total)) pageInfo.SetItems(items) common.ApiSuccess(c, pageInfo) } // AdminGetSupplierDetail godoc // @Summary 管理员查询供应商详情 // @Description 根据供应商ID查询供应商详情,返回申请人用户名 applicant_username // @Tags SupplierAdmin // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param id path int true "供应商ID" // @Success 200 {object} map[string]interface{} "供应商详情" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/supplier/{id} [get] func AdminGetSupplierDetail(c *gin.Context) { supplierID, err := strconv.Atoi(c.Param("id")) if err != nil || supplierID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的供应商ID"}) return } item, err := model.GetSupplierByID(supplierID) if err != nil { if model.IsSupplierApplicationNotFound(err) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "未找到供应商信息"}) return } common.ApiError(c, err) return } if err = attachSupplierCapability(item); err != nil { common.ApiError(c, err) return } response := gin.H{ "id": item.ID, "applicant_user_id": item.ApplicantUserID, "applicant_username": item.ApplicantUsername, "company_name": item.CompanyName, "credit_code": item.CreditCode, "business_license_url": item.BusinessLicenseURL, "business_license_file": item.BusinessLicenseFile, "company_logo_url": item.CompanyLogoURL, "supplier_type": item.SupplierType, "legal_representative": item.LegalRepresentative, "company_size": item.CompanySize, "contact_name": item.ContactName, "contact_mobile": item.ContactMobile, "contact_wechat": item.ContactWechat, "supplier_alias": item.SupplierAlias, "status": item.Status, "review_reason": item.ReviewReason, "reviewed_by": item.ReviewedBy, "reviewed_at": item.ReviewedAt, "created_at": item.CreatedAt, "updated_at": item.UpdatedAt, "supplier_capability": buildSupplierCapabilityResponse(item.SupplierCapability), } common.ApiSuccess(c, response) } // AdminUpdateSupplierApplication godoc // @Summary 管理员修改供应商申请资料 // @Description 管理员可修改任意供应商申请资料;审核通过(status=1)状态也允许修改,且修改后保持原状态 // @Tags SupplierAdmin // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param id path int true "供应商申请ID" // @Param request body SupplierApplicationSubmitRequest true "申请信息" // @Success 200 {object} map[string]interface{} "success + data{id,status}" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/supplier/application/{id} [put] func AdminUpdateSupplierApplication(c *gin.Context) { applicationID, err := strconv.Atoi(c.Param("id")) if err != nil || applicationID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的供应商申请ID"}) return } var req SupplierApplicationSubmitRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的参数"}) return } req.CompanyName = strings.TrimSpace(req.CompanyName) req.CreditCode = strings.TrimSpace(req.CreditCode) req.BusinessLicenseURL = strings.TrimSpace(req.BusinessLicenseURL) req.BusinessLicenseFile = strings.TrimSpace(req.BusinessLicenseFile) req.CompanyLogoURL = strings.TrimSpace(req.CompanyLogoURL) req.SupplierType = strings.TrimSpace(req.SupplierType) req.LegalRepresentative = strings.TrimSpace(req.LegalRepresentative) req.CompanySize = strings.TrimSpace(req.CompanySize) req.ContactName = strings.TrimSpace(req.ContactName) req.ContactMobile = strings.TrimSpace(req.ContactMobile) req.ContactWechat = strings.TrimSpace(req.ContactWechat) req.SupplierAlias = strings.TrimSpace(req.SupplierAlias) if req.CompanyName == "" || req.CreditCode == "" || req.BusinessLicenseURL == "" || req.CompanyLogoURL == "" || req.LegalRepresentative == "" || req.ContactName == "" || req.ContactMobile == "" || req.ContactWechat == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "请填写完整的必填字段"}) return } if req.SupplierType == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "请填写供应商类型"}) return } if !isValidSupplierType(req.SupplierType) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的供应商类型"}) return } if len(req.CreditCode) != 18 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "统一社会信用代码需为18位"}) return } aliasPtr := req.SupplierAlias app, err := model.AdminUpdateSupplierApplication(applicationID, &model.SupplierApplication{ CompanyName: req.CompanyName, CreditCode: req.CreditCode, BusinessLicenseURL: req.BusinessLicenseURL, BusinessLicenseFile: req.BusinessLicenseFile, CompanyLogoURL: req.CompanyLogoURL, SupplierType: req.SupplierType, LegalRepresentative: req.LegalRepresentative, CompanySize: req.CompanySize, ContactName: req.ContactName, ContactMobile: req.ContactMobile, ContactWechat: req.ContactWechat, SupplierAlias: &aliasPtr, }) if err != nil { if model.IsSupplierCreditCodeDuplicateError(err) { common.ApiErrorMsg(c, "统一社会信用代码已存在,请核对后重试") return } if model.IsSupplierApplicationNotFound(err) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "未找到可修改的供应商申请"}) return } if model.IsSupplierAliasDuplicateError(err) { common.ApiErrorMsg(c, "供应商别名已存在,请更换后重试") return } common.ApiError(c, err) return } common.ApiSuccess(c, gin.H{ "id": app.ID, "status": app.Status, }) } // AdminReviewSupplierApplication godoc // @Summary 管理员审核供应商申请 // @Description 任一管理员可审核一次,仅待审核状态允许处理 // @Tags SupplierAdmin // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param id path int true "申请ID" // @Param request body SupplierApplicationReviewRequest true "审核信息" // @Success 200 {object} map[string]interface{} "success + data{id,status}" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/supplier/application/{id}/review [post] func AdminReviewSupplierApplication(c *gin.Context) { applicationID, err := strconv.Atoi(c.Param("id")) if err != nil || applicationID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的申请ID"}) return } var req SupplierApplicationReviewRequest if err = c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的参数"}) return } if req.Status != model.SupplierApplicationStatusApproved && req.Status != model.SupplierApplicationStatusRejected { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "审核状态仅支持通过或驳回"}) return } req.Reason = strings.TrimSpace(req.Reason) req.SupplierType = strings.TrimSpace(req.SupplierType) if req.Status == model.SupplierApplicationStatusRejected && req.Reason == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "驳回时请填写原因"}) return } if req.Status == model.SupplierApplicationStatusApproved { capability, capabilityErr := model.GetSupplierCapabilityByApplicationID(applicationID) if capabilityErr != nil { if model.IsSupplierCapabilityNotFound(capabilityErr) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "审批通过前请先完善供应商技术能力信息"}) return } common.ApiError(c, capabilityErr) return } if !model.IsSupplierCapabilityComplete(capability) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "审批通过前请先完善供应商技术能力必填字段"}) return } } req.SupplierAlias = strings.TrimSpace(req.SupplierAlias) if req.Status == model.SupplierApplicationStatusApproved { if req.SupplierType == "" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "审批通过时必须选择供应商类型"}) return } if !isValidSupplierType(req.SupplierType) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的供应商类型"}) return } } app, err := model.ReviewSupplierApplication(applicationID, c.GetInt("id"), req.Status, req.Reason, req.SupplierAlias, req.SupplierType) if err != nil { if errors.Is(err, model.ErrSupplierApplicationAlreadyReviewed) { c.JSON(http.StatusOK, gin.H{"success": false, "message": "该申请已被其他管理员处理"}) return } if model.IsSupplierAliasDuplicateError(err) { c.JSON(http.StatusOK, gin.H{"success": false, "message": "供应商别名已存在,请更换后重试"}) return } common.ApiError(c, err) return } msgType := model.UserMessageTypeSupplierApproved msgTitle := "供应商入驻审核通过" msgContent := fmt.Sprintf("你的供应商申请“%s”已审核通过。", app.CompanyName) if app.Status == model.SupplierApplicationStatusRejected { msgType = model.UserMessageTypeSupplierRejected msgTitle = "供应商入驻审核驳回" msgContent = fmt.Sprintf("你的供应商申请“%s”已驳回,原因:%s", app.CompanyName, req.Reason) } _ = service.PublishUserMessage(&model.UserMessage{ ReceiverUserID: app.ApplicantUserID, ReceiverMinRole: 0, Type: msgType, Title: msgTitle, Content: msgContent, BizType: model.UserMessageBizTypeSupplierApplication, BizID: app.ID, }) common.ApiSuccess(c, gin.H{ "id": app.ID, "status": app.Status, }) } // ListMyMessages godoc // @Summary 查询当前用户站内消息 // @Tags Message // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param p query int false "页码" // @Param page_size query int false "每页数量" // @Param title query string false "标题模糊查询" // @Param read_status query string false "读取状态:all/read/unread,默认all" // @Success 200 {object} map[string]interface{} "分页结果" // @Router /user/messages/self [get] func ListMyMessages(c *gin.Context) { pageInfo := common.GetPageQuery(c) userID := c.GetInt("id") role := c.GetInt("role") titleKeyword := strings.TrimSpace(c.Query("title")) readStatus := strings.TrimSpace(c.Query("read_status")) if readStatus == "" { readStatus = "all" } if readStatus != "all" && readStatus != "read" && readStatus != "unread" { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的read_status参数"}) return } items, total, err := model.ListUserMessagesForUser(userID, role, pageInfo, titleKeyword, readStatus) if err != nil { common.ApiError(c, err) return } pageInfo.SetTotal(int(total)) pageInfo.SetItems(items) common.ApiSuccess(c, pageInfo) } // MarkMyMessageRead godoc // @Summary 标记当前用户消息为已读 // @Tags Message // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param id path int true "消息ID" // @Success 200 {object} map[string]interface{} "success + data{updated}" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/messages/{id}/read [post] func MarkMyMessageRead(c *gin.Context) { messageID, err := strconv.Atoi(c.Param("id")) if err != nil || messageID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的消息ID"}) return } ok, err := model.MarkUserMessageAsRead(messageID, c.GetInt("id"), c.GetInt("role")) if err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, gin.H{"updated": ok}) } // MarkAllMyMessagesRead godoc // @Summary 标记当前用户全部站内消息为已读 // @Tags Message // @Produce json // @Security CookieAuth // @Security ApiUserID // @Success 200 {object} map[string]interface{} "success + data{updated_count}" // @Router /user/messages/read_all [post] func MarkAllMyMessagesRead(c *gin.Context) { updatedCount, err := model.MarkAllUserMessagesAsRead(c.GetInt("id"), c.GetInt("role")) if err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, gin.H{"updated_count": updatedCount}) } // AdminPublishUserMessage godoc // @Summary 管理员发布站内消息 // @Description 支持按指定用户或按最小角色发布站内消息,至少设置 receiver_user_id 或 receiver_min_role 之一 // @Tags MessageAdmin // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param request body PublishUserMessageRequest true "消息内容" // @Success 200 {object} map[string]interface{} "success + data{published:true}" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/messages/publish [post] func AdminPublishUserMessage(c *gin.Context) { var req PublishUserMessageRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的参数"}) return } if req.ReceiverUserID <= 0 && req.ReceiverMinRole <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "请至少指定接收用户或角色门槛"}) return } msg := &model.UserMessage{ ReceiverUserID: req.ReceiverUserID, ReceiverMinRole: req.ReceiverMinRole, Type: req.Type, Title: req.Title, Content: req.Content, BizType: req.BizType, BizID: req.BizID, } if err := service.PublishUserMessage(msg); err != nil { common.ApiErrorMsg(c, err.Error()) return } common.ApiSuccess(c, gin.H{"published": true}) } // GetMyUnreadMessageCount godoc // @Summary 获取当前用户未读站内消息数量 // @Tags Message // @Produce json // @Security CookieAuth // @Security ApiUserID // @Success 200 {object} map[string]interface{} "success + data{unread_count}" // @Router /user/messages/unread_count [get] func GetMyUnreadMessageCount(c *gin.Context) { total, err := model.CountUnreadUserMessages(c.GetInt("id"), c.GetInt("role")) if err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, gin.H{"unread_count": total}) } // DeactivateMySupplierApplication godoc // @Summary 当前供应商注销 // @Description 仅审核通过状态可注销;注销后清空用户表 supplier_id 并将申请状态置为已注销 // @Tags Supplier // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param request body SupplierDeactivateRequest false "注销说明" // @Success 200 {object} map[string]interface{} "success + data{id,status}" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/supplier/application/deactivate [post] func DeactivateMySupplierApplication(c *gin.Context) { var req SupplierDeactivateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的参数"}) return } if req.SupplierID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "注销供应商时必须提供有效的supplier_id"}) return } reason := strings.TrimSpace(req.Reason) app, err := model.DeactivateSupplierApplication(c.GetInt("id"), c.GetInt("role"), req.SupplierID, reason) if err != nil { if errors.Is(err, model.ErrSupplierApplicationStatusNotApproved) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "当前供应商状态不支持注销"}) return } if model.IsSupplierApplicationNotFound(err) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "未找到可注销的供应商申请"}) return } common.ApiError(c, err) return } _ = service.PublishUserMessage(&model.UserMessage{ ReceiverUserID: 0, ReceiverMinRole: common.RoleAdminUser, Type: model.UserMessageTypeSupplierRejected, Title: "供应商已注销", Content: fmt.Sprintf("供应商“%s”已注销。", app.CompanyName), BizType: model.UserMessageBizTypeSupplierApplication, BizID: app.ID, }) common.ApiSuccess(c, gin.H{ "id": app.ID, "status": app.Status, }) } // ActivateSupplierApplication godoc // @Summary 管理员启用已注销供应商 // @Description 仅已注销状态可启用;启用后回填用户表 supplier_id 并将申请状态置为审核通过 // @Tags SupplierAdmin // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param request body SupplierDeactivateRequest false "启用说明" // @Success 200 {object} map[string]interface{} "success + data{id,status}" // @Failure 400 {object} map[string]interface{} "参数错误" // @Router /user/supplier/application/activate [post] func ActivateSupplierApplication(c *gin.Context) { var req SupplierDeactivateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的参数"}) return } if req.SupplierID <= 0 { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "启用供应商时必须提供有效的supplier_id"}) return } reason := strings.TrimSpace(req.Reason) app, err := model.ActivateSupplierApplication(c.GetInt("id"), c.GetInt("role"), req.SupplierID, reason) if err != nil { if errors.Is(err, model.ErrSupplierApplicationStatusNotDeactivated) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "当前供应商状态不支持启用"}) return } if model.IsSupplierApplicationNotFound(err) { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "未找到可启用的供应商申请"}) return } common.ApiError(c, err) return } _ = service.PublishUserMessage(&model.UserMessage{ ReceiverUserID: app.ApplicantUserID, ReceiverMinRole: 0, Type: model.UserMessageTypeSupplierApproved, Title: "供应商已启用", Content: fmt.Sprintf("你的供应商“%s”已重新启用。", app.CompanyName), BizType: model.UserMessageBizTypeSupplierApplication, BizID: app.ID, }) common.ApiSuccess(c, gin.H{ "id": app.ID, "status": app.Status, }) } // CreateMySupplierChannel godoc // @Summary 当前供应商新增渠道 // @Description 仅审核通过的供应商可新增,自动写入 owner_user_id 与 supplier_application_id // @Tags Supplier // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param request body AddChannelRequest true "渠道创建参数" // @Success 200 {object} map[string]interface{} "创建结果" // @Router /user/supplier/channels [post] func CreateMySupplierChannel(c *gin.Context) { supplierApp, ok := requireApprovedSupplierApplication(c) if !ok { return } addChannelRequest := AddChannelRequest{} if err := c.ShouldBindJSON(&addChannelRequest); err != nil { common.ApiError(c, err) return } if err := validateChannel(addChannelRequest.Channel, true); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()}) return } addChannelRequest.Channel.CreatedTime = common.GetTimestamp() keys := make([]string, 0) switch addChannelRequest.Mode { case "multi_to_single": addChannelRequest.Channel.ChannelInfo.IsMultiKey = true addChannelRequest.Channel.ChannelInfo.MultiKeyMode = addChannelRequest.MultiKeyMode if addChannelRequest.Channel.Type == constant.ChannelTypeVertexAi && addChannelRequest.Channel.GetOtherSettings().VertexKeyType != dto.VertexKeyTypeAPIKey { array, err := getVertexArrayKeys(addChannelRequest.Channel.Key) if err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()}) return } addChannelRequest.Channel.ChannelInfo.MultiKeySize = len(array) addChannelRequest.Channel.Key = strings.Join(array, "\n") } else { cleanKeys := make([]string, 0) for _, key := range strings.Split(addChannelRequest.Channel.Key, "\n") { if key == "" { continue } key = strings.TrimSpace(key) cleanKeys = append(cleanKeys, key) } addChannelRequest.Channel.ChannelInfo.MultiKeySize = len(cleanKeys) addChannelRequest.Channel.Key = strings.Join(cleanKeys, "\n") } keys = []string{addChannelRequest.Channel.Key} case "batch": if addChannelRequest.Channel.Type == constant.ChannelTypeVertexAi && addChannelRequest.Channel.GetOtherSettings().VertexKeyType != dto.VertexKeyTypeAPIKey { array, err := getVertexArrayKeys(addChannelRequest.Channel.Key) if err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()}) return } keys = array } else { keys = strings.Split(addChannelRequest.Channel.Key, "\n") } case "single": keys = []string{addChannelRequest.Channel.Key} default: c.JSON(http.StatusOK, gin.H{"success": false, "message": "不支持的添加模式"}) return } channels := make([]model.Channel, 0, len(keys)) for _, key := range keys { if key == "" { continue } localChannel := addChannelRequest.Channel localChannel.Key = key localChannel.OwnerUserID = c.GetInt("id") localChannel.SupplierApplicationID = supplierApp.ID if addChannelRequest.BatchAddSetKeyPrefix2Name && len(keys) > 1 { keyPrefix := localChannel.Key if len(localChannel.Key) > 8 { keyPrefix = localChannel.Key[:8] } localChannel.Name = fmt.Sprintf("%s %s", localChannel.Name, keyPrefix) } channels = append(channels, *localChannel) } if err := model.BatchInsertChannels(channels); err != nil { common.ApiError(c, err) return } service.ResetProxyClientCache() common.ApiSuccess(c, gin.H{"created": len(channels)}) } // ListMySupplierChannels godoc // @Summary 查询当前供应商渠道列表 // @Description 供应商返回本人渠道;管理员返回所有供应商渠道 // @Tags Supplier // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param p query int false "页码" // @Param page_size query int false "每页数量" // @Param channel_id query int false "渠道ID" // @Param name query string false "渠道名称(模糊)" // @Param key query string false "渠道密钥(精确或模糊)" // @Param base_url query string false "API地址(模糊)" // @Param model query string false "模型关键字(模糊)" // @Param group query string false "分组" // @Success 200 {object} map[string]interface{} "分页结果" // @Router /user/supplier/channels [get] func ListMySupplierChannels(c *gin.Context) { pageInfo := common.GetPageQuery(c) channelID, parseErr := model.ParseSupplierChannelIDFilter(c.Query("channel_id")) if parseErr != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "渠道ID参数格式错误"}) return } filter := model.SupplierChannelSearchFilter{ ChannelID: channelID, Name: strings.TrimSpace(c.Query("name")), Key: strings.TrimSpace(c.Query("key")), BaseURL: strings.TrimSpace(c.Query("base_url")), ModelKeyword: strings.TrimSpace(c.Query("model")), Group: strings.TrimSpace(c.Query("group")), } var ( items []*model.Channel total int64 err error ) if c.GetInt("role") >= common.RoleAdminUser { items, total, err = model.SearchSupplierChannels(nil, pageInfo.GetStartIdx(), pageInfo.GetPageSize(), filter) } else { ownerUserID := c.GetInt("id") items, total, err = model.SearchSupplierChannels(&ownerUserID, pageInfo.GetStartIdx(), pageInfo.GetPageSize(), filter) } if err != nil { common.ApiError(c, err) return } for _, item := range items { clearChannelInfo(item) } common.ApiSuccess(c, gin.H{ "items": items, "total": total, "page": pageInfo.GetPage(), "page_size": pageInfo.GetPageSize(), }) } // CreateMySupplierModel godoc // @Summary 当前供应商新增模型 // @Description 仅审核通过供应商可新增,自动写入 owner_user_id 与 supplier_application_id // @Tags Supplier // @Accept json // @Produce json // @Security CookieAuth // @Security ApiUserID // @Param request body model.Model true "模型创建参数" // @Success 200 {object} map[string]interface{} "创建结果" // @Router /user/supplier/models [post] func CreateMySupplierModel(c *gin.Context) { supplierApp, ok := requireApprovedSupplierApplication(c) if !ok { return } var m model.Model if err := c.ShouldBindJSON(&m); err != nil { common.ApiError(c, err) return } if m.ModelName == "" { common.ApiErrorMsg(c, "模型名称不能为空") return } dup, err := model.IsModelNameDuplicated(0, m.ModelName) if err != nil { common.ApiError(c, err) return } if dup { common.ApiErrorMsg(c, "模型名称已存在") return } m.OwnerUserID = c.GetInt("id") m.SupplierApplicationID = supplierApp.ID if err := m.Insert(); err != nil { common.ApiError(c, err) return } model.RefreshPricing() common.ApiSuccess(c, &m) } // ListMySupplierModels godoc // @Summary 查询当前供应商模型列表 // @Description 仅返回当前登录供应商创建的模型 // @Tags Supplier // @Produce json // @Security ApiKeyAuth // @Security ApiUserID // @Param p query int false "页码" // @Param page_size query int false "每页数量" // @Param model_name query string false "模型名称(模糊)" // @Param model_type query string false "模型类型(映射 vendor,支持名称或ID)" // @Success 200 {object} map[string]interface{} "分页结果" // @Router /user/supplier/models [get] func ListMySupplierModels(c *gin.Context) { pageInfo := common.GetPageQuery(c) keyword := strings.TrimSpace(c.Query("model_name")) vendor := strings.TrimSpace(c.Query("model_type")) var ( items []*model.Model total int64 err error ) if c.GetInt("role") >= common.RoleAdminUser { items, total, err = model.SearchSupplierModels(nil, keyword, vendor, pageInfo.GetStartIdx(), pageInfo.GetPageSize()) } else { ownerUserID := c.GetInt("id") items, total, err = model.SearchSupplierModels(&ownerUserID, keyword, vendor, pageInfo.GetStartIdx(), pageInfo.GetPageSize()) } if err != nil { common.ApiError(c, err) return } enrichModels(items) common.ApiSuccess(c, gin.H{ "items": items, "total": total, "page": pageInfo.GetPage(), "page_size": pageInfo.GetPageSize(), }) }