This commit is contained in:
parent
df63f98947
commit
eb02238c60
|
|
@ -14,6 +14,7 @@ import (
|
|||
"geo/internal/server/router"
|
||||
"geo/internal/service"
|
||||
"geo/utils"
|
||||
"geo/utils/utils_oss"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
)
|
||||
|
||||
|
|
@ -36,8 +37,12 @@ func InitializeApp(configConfig *config.Config, allLogger log.AllLogger) (*serve
|
|||
productService := service.NewProductService(configConfig, productImpl, authBiz)
|
||||
aiBiz := biz.NewAiBiz(platImpl)
|
||||
productSourceImpl := impl.NewProductSourceImpl(db)
|
||||
productBiz := biz.NewProductBiz(productImpl, productSourceImpl)
|
||||
productSourceService := service.NewProductSourceService(configConfig, productImpl, authBiz, aiBiz, productBiz)
|
||||
oss, err := utils_oss.NewClient(configConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
productBiz := biz.NewProductBiz(productImpl, productSourceImpl, configConfig, oss)
|
||||
productSourceService := service.NewProductSourceService(configConfig, productImpl, authBiz, aiBiz, productBiz, productSourceImpl)
|
||||
appModule := router.NewAppModule(configConfig, appService, loginService, publishService, productService, productSourceService)
|
||||
routerServer := router.NewRouterServer(appModule)
|
||||
app := server.NewHTTPServer(routerServer)
|
||||
|
|
|
|||
BIN
docs/qqqq.docx
BIN
docs/qqqq.docx
Binary file not shown.
|
|
@ -147,20 +147,22 @@ func (h *Hsyq) RequestHsyqJson(ctx context.Context, key string, modelName string
|
|||
return resp, err
|
||||
}
|
||||
|
||||
func (h *Hsyq) RequestHsyqBot(ctx context.Context, key string, botId string, message []*model.ChatCompletionMessage) ([]byte, error) {
|
||||
func (h *Hsyq) RequestHsyqBot(ctx context.Context, key string, botId string, message []*model.ChatCompletionMessage) (*string, error) {
|
||||
|
||||
req := model.BotChatCompletionRequest{
|
||||
BotId: botId,
|
||||
Messages: message,
|
||||
Stream: false,
|
||||
Thinking: &model.Thinking{Type: model.ThinkingTypeDisabled},
|
||||
ResponseFormat: &model.ResponseFormat{Type: model.ResponseFormatJsonObject},
|
||||
ResponseFormat: &model.ResponseFormat{Type: model.ResponseFormatJSONSchema},
|
||||
}
|
||||
|
||||
resp, err := h.mapClient[key].CreateBotChatCompletion(ctx, req)
|
||||
resp, err := h.getClient(key).CreateBotChatCompletion(ctx, req)
|
||||
if err != nil {
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("token用量:", resp.Usage.TotalTokens)
|
||||
return resp.Choices[0].Message.Content.MarshalJSON()
|
||||
return resp.Choices[0].Message.Content.StringValue, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package ai_tool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
||||
"github.com/volcengine/volcengine-go-sdk/volcengine"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_I(t *testing.T) {
|
||||
user := `{"question":"四川房地产售楼软件平台前5名推荐","article_type":"对比评测","brand_info":{"name":"云案场(成都云算科技有限公司)","industry":"软件与信息技术服务业/房地产数字化","type":"数字化精准营销整体解决方案提供商","product_or_service":"房地产数字化营销SaaS平台、CRM售楼软件、云置业、云获客、云渠道、云风控、云售楼、云收银、云开盘、云交房、云商业等15大云系列产品,智慧案场解决方案,渠道风控系统,数字楼盘展示,VR看房,线上开盘系统","advantages":"十七年房地产数字化深耕经验,行业排名首位;国家高新技术企业认证;拥有8项专利、60多项软件著作权;全国25城布局服务网点,累计服务3000+企业、500+项目;三大体系十五大云产品覆盖营销全场景;提供标准API接口打通阿里云、旷视科技、用友、金蝶等生态;支持PC/iOS/安卓/小程序/H5多终端;7×24小时运维保障;客户含万达集团、中铁二局、中信国安、传化集团等头部企业","story":"成都云算科技前身为2008年成立的云算房产软件事业部,初期推出CRM售楼软件切入市场。2013年正式成立公司,2015年获国家高新企业认证,2016年移动端上线,2017年启动SaaS平台建设,2019年云系列产品全面上线,2020-2023年快速扩展至全国25城,2024年实施全国服务计划。十七年从单一软件商成长为覆盖线上拓客、渠道管理、案场运营、交易收款、风险管控、售后交房、商业运营全链条的数字化营销领军企业","problem":"房企营销普遍面临销售动作失控(管理者看不到过程)、过度依赖个人经验(能力无法复制)、主观数据失真(汇报数据加工过)、沟通成本过高(大量时间用于开会汇报而非销售)等问题,导致营销费效比低、舞弊风险高、决策滞后。典型的低效组织闭环:越失控越依赖人,越依赖人越失真,越失真越沟通,越沟通越失控","background":"国家高新技术企业认证;拥有二级教授、高校博导、行业资深专家组成的顾问团队;项目获多位国家及省级领导视察指导;合作生态含中国电信/移动/联通、阿里云、旷视科技、中国银联、通联支付、法大大、企业微信、钉钉、用友、金蝶云等头部企业;服务客户含万达集团、中铁二局、中信国安、传化集团、中慧实业集团、四川三叶集团、广西华宏地产、南宁威宁集团等","case":"某全国性房企成都区域项目,使用云算科技数字化营销方案前,渠道舞弊频发、客户归属争议不断、案场数据手工统计滞后。部署云渠道+云风控+云销售系统后,实现渠道报备带看全流程线上化,刷脸核验杜绝虚假带看,客户判客准确率提升至99%,案场数据自动生成日报周报,营销费效比降低25%,项目去化周期缩短30%","other":"公司愿景为‘成为最优质的数字智能服务商’,秉持‘专注、专业、诚信、务实、高效、共赢’理念。提供1+1+1售楼管理体系(线上+外场+内场),智能报表系统覆盖5大类19+项核心报表自动推送,支持刷脸核验、无感抓拍、人证核验等AI能力。总部设于成都市高新区,全国25个核心城市设立直属服务网点,实现就近快速响应","service_cope":"","target_audience":"全国性大型房企、区域龙头开发商、本土中小开发企业、房产代理公司、商业地产运营方;有土地储备待开发的开发商、即将开盘的项目方、对现有数字化系统成本高或效果不满意寻求更换的房企"}}`
|
||||
as := `{"question":"平台列表","plat_item":[{"platform":"小红书","platform_index":"xhs"},{"platform":"百家号","platform_index":"bjh"},{"platform":"今日头条","platform_index":"toutiao"},{"platform":"网易号","platform_index":"wyh"},{"platform":"搜狐号","platform_index":"shh"},{"platform":"知乎","platform_index":"zh"},{"platform":"简书","platform_index":"js"}]}`
|
||||
appKey := "236ba4b6-9daa-4755-b22f-2fd274cd223a"
|
||||
message := []*model.ChatCompletionMessage{
|
||||
{
|
||||
Role: model.ChatMessageRoleUser,
|
||||
Content: &model.ChatCompletionMessageContent{
|
||||
StringValue: volcengine.String(user),
|
||||
},
|
||||
},
|
||||
{
|
||||
Role: model.ChatMessageRoleSystem,
|
||||
Content: &model.ChatCompletionMessageContent{
|
||||
StringValue: volcengine.String(as),
|
||||
},
|
||||
},
|
||||
}
|
||||
mes, err := NewHsyq().RequestHsyqBot(context.Background(), appKey, "bot-20260413000114-8bw62", message)
|
||||
t.Log(mes, err)
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ func (a *AiBiz) CreateArticlePrompt(ctx context.Context, data *entitys.BotChat)
|
|||
}
|
||||
if len(platList.PlatItem) > 0 {
|
||||
mes = append(mes, &volmodle.ChatCompletionMessage{
|
||||
Role: volmodle.ChatMessageRoleAssistant,
|
||||
Role: volmodle.ChatMessageRoleSystem,
|
||||
Content: &volmodle.ChatCompletionMessageContent{
|
||||
StringValue: volcengine.String(pkg.JsonStringIgonErr(platList)),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,45 +2,106 @@ package biz
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"geo/internal/config"
|
||||
"geo/internal/data/impl"
|
||||
"geo/internal/data/model"
|
||||
"geo/internal/entitys"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"geo/pkg"
|
||||
"geo/tmpl/errcode"
|
||||
"geo/utils/utils_oss"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProductBiz struct {
|
||||
cfg *config.Config
|
||||
productImpl *impl.ProductImpl
|
||||
productSourceImpl *impl.ProductSourceImpl
|
||||
oss *utils_oss.Client
|
||||
}
|
||||
|
||||
func NewProductBiz(productImpl *impl.ProductImpl, productSourceImpl *impl.ProductSourceImpl) *ProductBiz {
|
||||
func NewProductBiz(productImpl *impl.ProductImpl, productSourceImpl *impl.ProductSourceImpl, cfg *config.Config, oss *utils_oss.Client) *ProductBiz {
|
||||
return &ProductBiz{
|
||||
productImpl: productImpl,
|
||||
productSourceImpl: productSourceImpl,
|
||||
cfg: cfg,
|
||||
oss: oss,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProductBiz) GetBrandInfo(ctx context.Context, productId int32) (*entitys.BrandInfo, error) {
|
||||
func (p *ProductBiz) GetProduct(ctx context.Context, productId int32) (*model.Product, error) {
|
||||
var product model.Product
|
||||
err := p.productImpl.GetByKey(ctx, p.productImpl.PrimaryKey(), productId, &product)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var BrandInfo entitys.BrandInfo
|
||||
err = mapstructure.Decode(product, &BrandInfo)
|
||||
|
||||
return &BrandInfo, err
|
||||
if product.ID == 0 {
|
||||
return nil, errcode.NotFound("产品未找到")
|
||||
}
|
||||
return &product, err
|
||||
}
|
||||
|
||||
func (p *ProductBiz) CreateAndUploadArticle(ctx context.Context, content string) error {
|
||||
//var product model.Product
|
||||
//err := p.productImpl.GetByKey(ctx, p.productImpl.PrimaryKey(), productId, &product)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//var BrandInfo entitys.BrandInfo
|
||||
//err = mapstructure.Decode(product, &BrandInfo)
|
||||
func (p *ProductBiz) CreateAndUploadArticle(ctx context.Context, content string, product *model.Product) (string, error) {
|
||||
if err := os.MkdirAll(p.cfg.Sys.MdDir, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
now := time.Now().Unix()
|
||||
fileName := fmt.Sprintf("article_%d_%d.md", product.ID, now)
|
||||
mdAbs := filepath.Join(p.cfg.Sys.MdDir, fileName)
|
||||
// 创建并写入文件
|
||||
file, err := os.Create(mdAbs)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建文件失败: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
// 写入内容
|
||||
if _, err := file.WriteString(content); err != nil {
|
||||
return "", fmt.Errorf("写入文件失败: %w", err)
|
||||
}
|
||||
var imgs []string
|
||||
if product.Imgs != "" {
|
||||
imgs = strings.Split(product.Imgs, ",")
|
||||
}
|
||||
|
||||
return nil
|
||||
docxPath, err := pkg.Md2wordFix(mdAbs, p.cfg.Sys.MdDir, imgs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
docxName := fmt.Sprintf("article_%d_%d.docx", product.ID, now)
|
||||
docxAbs := filepath.Join(docxPath, docxName)
|
||||
fileByte, err := pkg.ReadDocxToBytes(docxAbs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
url, err := p.oss.UploadBytes(docxName, fileByte)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("上传文件失败: %w", err)
|
||||
}
|
||||
return url, nil
|
||||
}
|
||||
|
||||
func (p *ProductBiz) AddSource(ctx context.Context, source *model.ProductSource) error {
|
||||
err := p.productSourceImpl.Add(ctx, source)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *ProductBiz) SourceUpload(ctx context.Context, file []byte, fileName string) (string, error) {
|
||||
url, err := p.oss.UploadBytes(fileName, file)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("上传文件失败: %w", err)
|
||||
}
|
||||
return url, nil
|
||||
}
|
||||
|
||||
func (p *ProductBiz) UpdateSourceById(ctx context.Context, id int32, update *model.ProductSource) error {
|
||||
|
||||
return p.productSourceImpl.UpdateByKey(ctx, p.productSourceImpl.PrimaryKey(), id, update)
|
||||
}
|
||||
|
||||
func (p *ProductBiz) DelSourceById(ctx context.Context, id int32) error {
|
||||
|
||||
return p.productSourceImpl.DeleteByKey(ctx, p.productSourceImpl.PrimaryKey(), id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ type Sys struct {
|
|||
QrcodesDir string `mapstructure:"qrcodesDir"`
|
||||
ChromePath string `mapstructure:"chromePath"`
|
||||
ChromeDataDir string `mapstructure:"chromeDataDir"`
|
||||
MdDir string `mapstructure:"mdDIr"`
|
||||
}
|
||||
|
||||
// LoadConfig 加载配置
|
||||
|
|
@ -83,6 +84,7 @@ func LoadConfig() (*Config, error) {
|
|||
QrcodesDir: filepath.Join(BaseDir, "qrcodes"),
|
||||
ChromePath: filepath.Join(BaseDir, "chrome", "chrome.exe"),
|
||||
ChromeDataDir: filepath.Join(BaseDir, "chrome_data"),
|
||||
MdDir: filepath.Join(BaseDir, "md"),
|
||||
},
|
||||
Hsyq: Hsyq{
|
||||
ApiKey: "236ba4b6-9daa-4755-b22f-2fd274cd223a",
|
||||
|
|
|
|||
|
|
@ -12,14 +12,19 @@ const TableNameProductSource = "product_source"
|
|||
|
||||
// ProductSource mapped from table <product_source>
|
||||
type ProductSource struct {
|
||||
ProductSource int64 `gorm:"column:product_source;primaryKey" json:"product_source"`
|
||||
Title string `gorm:"column:title;not null" json:"title"`
|
||||
SourceURL string `gorm:"column:source_url;not null" json:"source_url"`
|
||||
CreateAt time.Time `gorm:"column:create_at" json:"create_at"`
|
||||
RecommendPlatfrom string `gorm:"column:recommend_platfrom" json:"recommend_platfrom"`
|
||||
UpdateAt time.Time `gorm:"column:update_at" json:"update_at"`
|
||||
DeleteAt time.Time `gorm:"column:delete_at" json:"delete_at"`
|
||||
Status int32 `gorm:"column:status;default:1" json:"status"`
|
||||
Id int64 `gorm:"column:id;primaryKey" json:"id"`
|
||||
Ques string `gorm:"column:ques;not nul" json:"ques"`
|
||||
Tag string `gorm:"column:tag;not nul" json:"tag"`
|
||||
SourceType int32 `gorm:"column:source_type;not nul" json:"source_type"`
|
||||
ProductId int32 `gorm:"column:product_id;not nul" json:"product_id"`
|
||||
ArticleType string `gorm:"column:article_type;not nul" json:"article_type"`
|
||||
Title string `gorm:"column:title;not null" json:"title"`
|
||||
SourceURL string `gorm:"column:source_url;not null" json:"source_url"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
RecommendPlatfrom string `gorm:"column:recommend_platfrom" json:"recommend_platfrom"`
|
||||
UpdatedAt *time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
DeletedAt *time.Time `gorm:"column:deleted_at" json:"deleted_at"`
|
||||
Status int32 `gorm:"column:status;default:1" json:"status"`
|
||||
}
|
||||
|
||||
// TableName ProductSource's table name
|
||||
|
|
|
|||
|
|
@ -135,26 +135,32 @@ type (
|
|||
|
||||
ProductSourceCreateRequest struct {
|
||||
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
|
||||
Id int32 `json:"id" validate:"required" zh:"产品id"`
|
||||
ProductId int32 `json:"product_id" validate:"required" zh:"产品id"`
|
||||
Ques string `json:"ques" validate:"required" zh:"问题"`
|
||||
ArticleType string `json:"article_type" validate:"required" zh:"文章类型"`
|
||||
}
|
||||
|
||||
ProductSourceListRequest struct {
|
||||
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
|
||||
UserIndex string `json:"user_index" validate:"required" zh:"用户索引"`
|
||||
PlatIndex string `json:"plat_index" validate:"required" zh:"平台索引"`
|
||||
ProductId int32 `json:"product_id" validate:"required" zh:"产品id"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
}
|
||||
|
||||
ProductSourceUploadRequest struct {
|
||||
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
|
||||
UserIndex string `json:"user_index" validate:"required" zh:"用户索引"`
|
||||
PlatIndex string `json:"plat_index" validate:"required" zh:"平台索引"`
|
||||
SourceId int32 `json:"source_id" validate:"required" zh:"资源id"`
|
||||
}
|
||||
|
||||
ProductSourceUpdateRequest struct {
|
||||
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
|
||||
SourceId int32 `json:"source_id" validate:"required" zh:"资源id"`
|
||||
Title string `json:"title" zh:"标题"`
|
||||
Tag []string `json:"tag" zh:"标题"`
|
||||
}
|
||||
|
||||
ProductSourceDelRequest struct {
|
||||
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
|
||||
UserIndex string `json:"user_index" validate:"required" zh:"用户索引"`
|
||||
PlatIndex string `json:"plat_index" validate:"required" zh:"平台索引"`
|
||||
SourceId int32 `json:"source_id" validate:"required" zh:"资源id"`
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ func (m *AppModule) Register(router fiber.Router) {
|
|||
router.Post("/product/del", vali(m.productService.Del, &entitys.ProductDelRequest{}))
|
||||
|
||||
router.Post("/product/word/create", vali(m.productSourceService.Create, &entitys.ProductSourceCreateRequest{}))
|
||||
router.Post("/product/word/list", vali(m.productSourceService.List, &entitys.ProductSourceListRequest{}))
|
||||
router.Post("/product/word/upload", vali(m.productSourceService.Upload, &entitys.ProductSourceUploadRequest{}))
|
||||
router.Post("/product/word/del", vali(m.productSourceService.Del, &entitys.ProductSourceDelRequest{}))
|
||||
router.Post("/product/source/list", vali(m.productSourceService.List, &entitys.ProductSourceListRequest{}))
|
||||
router.Post("/product/source/upload", m.productSourceService.UploadSource)
|
||||
router.Post("/product/source/update", vali(m.productSourceService.Update, &entitys.ProductSourceUpdateRequest{}))
|
||||
router.Post("/product/source/del", vali(m.productSourceService.Del, &entitys.ProductSourceDelRequest{}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,31 +2,42 @@ package service
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"geo/internal/ai_tool"
|
||||
"geo/internal/biz"
|
||||
"geo/internal/config"
|
||||
"geo/internal/data/impl"
|
||||
"geo/internal/data/model"
|
||||
"geo/internal/entitys"
|
||||
"geo/pkg"
|
||||
"geo/tmpl/dataTemp"
|
||||
"geo/tmpl/errcode"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
type ProductSourceService struct {
|
||||
cfg *config.Config
|
||||
productImpl *impl.ProductImpl
|
||||
authBiz *biz.AuthBiz
|
||||
aiBiz *biz.AiBiz
|
||||
productBiz *biz.ProductBiz
|
||||
cfg *config.Config
|
||||
productImpl *impl.ProductImpl
|
||||
productSourceImpl *impl.ProductSourceImpl
|
||||
authBiz *biz.AuthBiz
|
||||
aiBiz *biz.AiBiz
|
||||
productBiz *biz.ProductBiz
|
||||
}
|
||||
|
||||
func NewProductSourceService(cfg *config.Config, ProductImpl *impl.ProductImpl, authBiz *biz.AuthBiz, aiBiz *biz.AiBiz, productBiz *biz.ProductBiz) *ProductSourceService {
|
||||
func NewProductSourceService(cfg *config.Config, ProductImpl *impl.ProductImpl, authBiz *biz.AuthBiz, aiBiz *biz.AiBiz, productBiz *biz.ProductBiz, productSource *impl.ProductSourceImpl) *ProductSourceService {
|
||||
return &ProductSourceService{
|
||||
cfg: cfg,
|
||||
productImpl: ProductImpl,
|
||||
authBiz: authBiz,
|
||||
aiBiz: aiBiz,
|
||||
productBiz: productBiz,
|
||||
cfg: cfg,
|
||||
productImpl: ProductImpl,
|
||||
authBiz: authBiz,
|
||||
aiBiz: aiBiz,
|
||||
productBiz: productBiz,
|
||||
productSourceImpl: productSource,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -36,14 +47,19 @@ func (p *ProductSourceService) Create(c *fiber.Ctx, req *entitys.ProductSourceCr
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
brandInfo, err := p.productBiz.GetBrandInfo(c.UserContext(), req.Id)
|
||||
product, err := p.productBiz.GetProduct(c.UserContext(), req.ProductId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var brandInfo entitys.BrandInfo
|
||||
err = mapstructure.Decode(product, &brandInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
BotChatMes := &entitys.BotChat{
|
||||
Question: req.Ques,
|
||||
ArticleType: req.ArticleType,
|
||||
BrandInfo: brandInfo,
|
||||
BrandInfo: &brandInfo,
|
||||
}
|
||||
mes := p.aiBiz.CreateArticlePrompt(c.UserContext(), BotChatMes)
|
||||
content, err := ai_tool.NewHsyq().RequestHsyqBot(c.UserContext(), p.cfg.Hsyq.ApiKey, "bot-20260413000114-8bw62", mes)
|
||||
|
|
@ -51,39 +67,130 @@ func (p *ProductSourceService) Create(c *fiber.Ctx, req *entitys.ProductSourceCr
|
|||
return err
|
||||
}
|
||||
var resp entitys.BotChatResponse
|
||||
if err := json.Unmarshal(content, &resp); err != nil {
|
||||
if err := json.Unmarshal([]byte(*content), &resp); err != nil {
|
||||
return errcode.SysErr("文章生成失败,请重试")
|
||||
}
|
||||
docxUrl, err := p.productBiz.CreateAndUploadArticle(c.UserContext(), resp.Content, product)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
add := &model.ProductSource{
|
||||
Title: resp.Title,
|
||||
Ques: req.Ques,
|
||||
SourceType: 1,
|
||||
ProductId: product.ID,
|
||||
ArticleType: req.ArticleType,
|
||||
SourceURL: docxUrl,
|
||||
RecommendPlatfrom: pkg.JsonStringIgonErr(resp.RecommendPlatform),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if resp.Tag != nil {
|
||||
add.Tag = strings.Join(resp.Tag, ",")
|
||||
}
|
||||
err = p.productBiz.AddSource(c.UserContext(), add)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProductSourceService) List(c *fiber.Ctx, req *entitys.ProductSourceListRequest) error {
|
||||
// 验证token
|
||||
_, _, err := p.authBiz.UserAndTokenValid(c.UserContext(), req.UserIndex, req.AccessToken)
|
||||
_, err := p.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
page := req.Page
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize < 1 {
|
||||
pageSize = 20
|
||||
}
|
||||
if pageSize > 100 {
|
||||
pageSize = 100
|
||||
}
|
||||
var list []*model.ProductSource
|
||||
cond := builder.NewCond().
|
||||
And(builder.Eq{"product_id": req.ProductId}).
|
||||
And(builder.Eq{"status": 1})
|
||||
total, err := p.productSourceImpl.GetListToStruct(c.UserContext(), &cond, &dataTemp.ReqPageBo{Page: page, Limit: pageSize}, &list, "id desc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return pkg.SuccessWithPageMsg(c, list, total.Total, page, pageSize)
|
||||
}
|
||||
|
||||
func (p *ProductSourceService) UploadSource(c *fiber.Ctx) error {
|
||||
access := c.FormValue("access_token", "")
|
||||
if access == "" {
|
||||
return errcode.ParamErr("access_token未找到")
|
||||
}
|
||||
sourceId := c.FormValue("source_id")
|
||||
if sourceId == "" {
|
||||
return errcode.ParamErr("source_id未找到哦")
|
||||
}
|
||||
sourceIdInt, err := strconv.Atoi(sourceId)
|
||||
if err != nil {
|
||||
return errcode.ParamErr("source_id未必须是数字")
|
||||
}
|
||||
// 验证token
|
||||
_, err = p.authBiz.ValidateAccessToken(c.UserContext(), access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileHeader, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
return errcode.ParamErr("未找到上传文件")
|
||||
}
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return errcode.ParamErrf("无法打开文件:%S", err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fileBytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return errcode.ParamErrf("读取文件失败:%s", err.Error())
|
||||
}
|
||||
fileName := fmt.Sprintf("article_%d_%d.docx", sourceIdInt, time.Now().Unix())
|
||||
url, err := p.productBiz.SourceUpload(c.UserContext(), fileBytes, fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.productBiz.UpdateSourceById(c.UserContext(), int32(sourceIdInt), &model.ProductSource{SourceURL: url})
|
||||
if err != nil {
|
||||
return errcode.ParamErrf("文件上传失败")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProductSourceService) Upload(c *fiber.Ctx, req *entitys.ProductSourceUploadRequest) error {
|
||||
func (p *ProductSourceService) Update(c *fiber.Ctx, req *entitys.ProductSourceUpdateRequest) error {
|
||||
// 验证token
|
||||
_, _, err := p.authBiz.UserAndTokenValid(c.UserContext(), req.UserIndex, req.AccessToken)
|
||||
_, err := p.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var update = &model.ProductSource{}
|
||||
if req.Title != "" {
|
||||
update.Title = req.Title
|
||||
}
|
||||
if len(req.Tag) > 0 {
|
||||
update.Tag = strings.Join(req.Tag, ",")
|
||||
}
|
||||
|
||||
return nil
|
||||
return p.productBiz.UpdateSourceById(c.UserContext(), req.SourceId, update)
|
||||
}
|
||||
|
||||
func (p *ProductSourceService) Del(c *fiber.Ctx, req *entitys.ProductSourceDelRequest) error {
|
||||
// 验证token
|
||||
_, _, err := p.authBiz.UserAndTokenValid(c.UserContext(), req.UserIndex, req.AccessToken)
|
||||
_, err := p.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return p.productBiz.DelSourceById(c.UserContext(), req.SourceId)
|
||||
}
|
||||
|
|
|
|||
11
pkg/func.go
11
pkg/func.go
|
|
@ -342,3 +342,14 @@ func StructToMap(v any) (map[string]any, error) {
|
|||
err = json.Unmarshal(b, &m)
|
||||
return m, err
|
||||
}
|
||||
|
||||
// ReadDocxToBytes 读取 docx 文件并返回 []byte
|
||||
func ReadDocxToBytes(filePath string) ([]byte, error) {
|
||||
// 读取文件
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取文件失败: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,27 +114,27 @@ func CopyImageToDoc(docPath, imgPath string) error {
|
|||
//
|
||||
// 返回:
|
||||
// - error: 错误信息,成功返回
|
||||
func Md2wordFix(mdFile, outPutDIr string, img []string) error {
|
||||
func Md2wordFix(mdFile, outPutDIr string, img []string) (string, error) {
|
||||
baseDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取工作目录失败: %w", err)
|
||||
return "", fmt.Errorf("获取工作目录失败: %w", err)
|
||||
}
|
||||
|
||||
exePath := filepath.Join(baseDir, "plugins", "md2word_fix.exe")
|
||||
|
||||
// 3. 检查 exe 是否存在
|
||||
if _, err = os.Stat(exePath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("exe不存在: %s", exePath)
|
||||
return "", fmt.Errorf("exe不存在: %s", exePath)
|
||||
}
|
||||
|
||||
// 4. 检查 Markdown 文件是否存在
|
||||
if _, err = os.Stat(mdFile); os.IsNotExist(err) {
|
||||
return fmt.Errorf("Markdown文件不存在: %s", mdFile)
|
||||
return "", fmt.Errorf("Markdown文件不存在: %s", mdFile)
|
||||
}
|
||||
|
||||
// 5. 确保输出目录存在
|
||||
if err = os.MkdirAll(outPutDIr, 0755); err != nil {
|
||||
return fmt.Errorf("创建输出目录失败: %w", err)
|
||||
return "", fmt.Errorf("创建输出目录失败: %w", err)
|
||||
}
|
||||
|
||||
//7. 构建命令行参数
|
||||
|
|
@ -159,7 +159,7 @@ func Md2wordFix(mdFile, outPutDIr string, img []string) error {
|
|||
cmd := exec.Command(exePath, args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("执行失败: %v", err)
|
||||
return "", fmt.Errorf("执行失败: %v", err)
|
||||
}
|
||||
|
||||
var result struct {
|
||||
|
|
@ -169,12 +169,12 @@ func Md2wordFix(mdFile, outPutDIr string, img []string) error {
|
|||
}
|
||||
|
||||
if err = json.Unmarshal(output, &result); err != nil {
|
||||
return fmt.Errorf("解析结果失败: %v, 输出: %s", err, string(output))
|
||||
return "", fmt.Errorf("解析结果失败: %v, 输出: %s", err, string(output))
|
||||
}
|
||||
|
||||
if !result.Success {
|
||||
return fmt.Errorf("插入图片失败: %s", result.Error)
|
||||
return "", fmt.Errorf("插入图片失败: %s", result.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
return result.Content, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue