This commit is contained in:
renzhiyuan 2026-04-15 01:01:26 +08:00
parent eb02238c60
commit 7eda42b8a7
26 changed files with 279 additions and 231 deletions

View File

@ -28,21 +28,22 @@ func InitializeApp(configConfig *config.Config, allLogger log.AllLogger) (*serve
platImpl := impl.NewPlatImpl(db)
publishImpl := impl.NewPublishImpl(db)
loginRelationImpl := impl.NewLoginRelationImpl(db)
articleTypeImpl := impl.NewArticleTypeImpl(db)
publishBiz := biz.NewPublishBiz(configConfig, publishImpl, userImpl, platImpl, tokenImpl, loginRelationImpl)
authBiz := biz.NewAuthBiz(configConfig, tokenImpl, userImpl)
appService := service.NewAppService(configConfig, tokenImpl, userImpl, platImpl, publishBiz, authBiz, loginRelationImpl)
loginService := service.NewLoginService(configConfig, publishBiz, authBiz)
publishService := service.NewPublishService(configConfig, publishBiz, authBiz, db)
productImpl := impl.NewProductImpl(db)
productService := service.NewProductService(configConfig, productImpl, authBiz)
aiBiz := biz.NewAiBiz(platImpl)
productSourceImpl := impl.NewProductSourceImpl(db)
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)
productService := service.NewProductService(configConfig, productImpl, authBiz, productBiz)
aiBiz := biz.NewAiBiz(platImpl)
productSourceService := service.NewProductSourceService(configConfig, productImpl, authBiz, aiBiz, productBiz, productSourceImpl, publishBiz, articleTypeImpl)
appModule := router.NewAppModule(configConfig, appService, loginService, publishService, productService, productSourceService)
routerServer := router.NewRouterServer(appModule)
app := server.NewHTTPServer(routerServer)

View File

@ -52,6 +52,7 @@ func (p *ProductBiz) CreateAndUploadArticle(ctx context.Context, content string,
mdAbs := filepath.Join(p.cfg.Sys.MdDir, fileName)
// 创建并写入文件
file, err := os.Create(mdAbs)
defer os.Remove(mdAbs)
if err != nil {
return "", fmt.Errorf("创建文件失败: %w", err)
}
@ -66,6 +67,7 @@ func (p *ProductBiz) CreateAndUploadArticle(ctx context.Context, content string,
}
docxPath, err := pkg.Md2wordFix(mdAbs, p.cfg.Sys.MdDir, imgs)
defer os.Remove(docxPath)
if err != nil {
return "", err
}
@ -76,7 +78,7 @@ func (p *ProductBiz) CreateAndUploadArticle(ctx context.Context, content string,
return "", err
}
url, err := p.oss.UploadBytes(docxName, fileByte)
url, err := p.SourceUpload(ctx, fileByte, docxName)
if err != nil {
return "", fmt.Errorf("上传文件失败: %w", err)
}
@ -89,7 +91,7 @@ func (p *ProductBiz) AddSource(ctx context.Context, source *model.ProductSource)
}
func (p *ProductBiz) SourceUpload(ctx context.Context, file []byte, fileName string) (string, error) {
url, err := p.oss.UploadBytes(fileName, file)
url, err := p.oss.UploadBytes(p.cfg.Oss.FilePath+fileName, file)
if err != nil {
return "", fmt.Errorf("上传文件失败: %w", err)
}

View File

@ -20,6 +20,7 @@ type Oss struct {
Bucket string `mapstructure:"bucket"`
Domain string `mapstructure:"domain"`
Endpoint string `mapstructure:"endpoint"`
FilePath string `mapstructure:"filepath"`
}
// ServerConfig 服务器配置
@ -95,6 +96,7 @@ func LoadConfig() (*Config, error) {
Bucket: "attachment-public",
Domain: "https://attachment-public.oss-cn-hangzhou.aliyuncs.com",
Endpoint: "https://oss-cn-hangzhou.aliyuncs.com",
FilePath: "geo/",
},
}, nil
}

View File

@ -203,7 +203,7 @@ func (p *PublishImpl) GetListWithUser(ctx context.Context, tokenID int32, page,
`).
Joins("LEFT JOIN user u ON p.user_index COLLATE utf8mb4_unicode_ci = u.user_index").
Joins("LEFT JOIN plat pl ON p.plat_index COLLATE utf8mb4_unicode_ci = pl.index").
Where("u.token_id = ?", tokenID)
Where("u.token_id = ?", tokenID).Order("publish_time desc")
// 添加过滤条件
if userIndex, ok := filters["user_index"]; ok && userIndex != "" {

View File

@ -26,7 +26,7 @@ type Product struct {
Background string `gorm:"column:background;comment:信任背书" json:"background"` // 信任背书
Case string `gorm:"column:case;comment:品牌案例" json:"case"` // 品牌案例
Other string `gorm:"column:other;comment:其他信息" json:"other"` // 其他信息
ServiceCope string `gorm:"column:service_cope;comment:服务范围" json:"service_cope"` // 服务范围
ServiceScope string `gorm:"column:service_scope;comment:服务范围" json:"service_scope"` // 服务范围
TargetAudience string `gorm:"column:target_audience;comment:目标客户群体" json:"target_audience"` // 目标客户群体
Imgs string `gorm:"column:imgs;comment:图片" json:"imgs"` // 图片
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`

View File

@ -14,6 +14,7 @@ const TableNamePublish = "publish"
type Publish struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
TokenID int32 `gorm:"column:token_id;not null" json:"token_id"`
SourceID int32 `gorm:"column:source_id;not null" json:"source_id"`
UserIndex string `gorm:"column:user_index;not null;comment:关联user.user_index" json:"user_index"` // 关联user.user_index
RequestID string `gorm:"column:request_id;not null;comment:日志id" json:"request_id"` // 日志id
Title string `gorm:"column:title;not null" json:"title"`

View File

@ -24,6 +24,11 @@ type (
UserIndex string `json:"user_index" validate:"required" zh:"用户索引"`
}
PlatListRequest struct {
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
PlatType int `json:"plat_type" zh:"平台类型"`
}
PublishRecordsRequest struct {
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
Records []PublishRecordItem `json:"records" validate:"required" zh:"发布记录"`
@ -100,7 +105,8 @@ type (
Background string `json:"background" zh:"信任背书"`
Case string `json:"case" zh:"品牌案例"`
Other string `json:"other" zh:"其他信息"`
ServiceCope string `json:"service_cope" zh:"服务范围"`
ServiceScope string `json:"service_scope" zh:"服务范围"`
Imgs string `json:"imgs" zh:"图片"`
TargetAudience string `json:"target_audience" zh:"目标客户群体"`
}
@ -111,6 +117,11 @@ type (
PageSize int `json:"page_size"`
}
ProductDetailRequest struct {
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
Id int32 `json:"id" validate:"required" zh:"id"`
}
ProductUpdateRequest struct {
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
Id int32 `json:"id" validate:"required" zh:"id"`
@ -125,6 +136,7 @@ type (
Case string `json:"case" zh:"品牌案例"`
Other string `json:"other" zh:"其他信息"`
ServiceCope string `json:"service_cope" zh:"服务范围"`
Imgs string `json:"imgs" zh:"图片"`
TargetAudience string `json:"target_audience" zh:"目标客户群体"`
}
@ -133,6 +145,11 @@ type (
Id int32 `json:"id" validate:"required" zh:"产品id"`
}
ImageUploadRequest struct {
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
Id int32 `json:"id" validate:"required" zh:"产品id"`
}
ProductSourceCreateRequest struct {
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
ProductId int32 `json:"product_id" validate:"required" zh:"产品id"`
@ -153,14 +170,21 @@ type (
}
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:"标题"`
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"`
SourceId int32 `json:"source_id" validate:"required" zh:"资源id"`
}
ProductPublishRequest struct {
AccessToken string `json:"access_token" validate:"required" zh:"access_token"`
SourceId int32 `json:"source_id" validate:"required" zh:"资源id"`
Plat []string `json:"plat" validate:"required" zh:"平台"`
PublishTime string `json:"publish_time" validate:"required" zh:"发布时间"`
}
)

View File

@ -29,23 +29,6 @@ func (p *BaijiahaoPublisher) CheckLoginStatus() bool {
return true
}
func (p *BaijiahaoPublisher) CheckLogin() (bool, string) {
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
p.Page.MustNavigate(p.EditorURL)
p.Sleep(3)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *BaijiahaoPublisher) WaitLogin() (bool, string) {
if err := p.SetupDriver(); err != nil {

View File

@ -333,10 +333,6 @@ func (p *BasePublisher) CheckLoginStatus() bool {
return true
}
func (b *BasePublisher) CheckLogin() (bool, string) {
return false, "需要实现"
}
func (b *BasePublisher) PublishNote() (bool, string) {
return false, "需要实现"
}

View File

@ -19,25 +19,6 @@ func NewCSDNPublisher(ctx context.Context, task *TaskParams, cfg *config.Config,
return &CSDNPublisher{NewBasePublisher(ctx, task, cfg, logger)}
}
func (p *CSDNPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Close()
p.Page.MustNavigate(p.EditorURL)
p.Sleep(2)
p.WaitForPageReady(3)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *CSDNPublisher) WaitLogin() (bool, string) {
p.LogInfo("开始等待登录...")

View File

@ -21,25 +21,6 @@ func NewDouyinSpPublisher(ctx context.Context, task *TaskParams, cfg *config.Con
return &DouyinSpPublisher{NewBasePublisher(ctx, task, cfg, logger)}
}
func (p *DouyinSpPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Close()
p.Page.MustNavigate(p.EditorURL)
p.Sleep(3)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *DouyinSpPublisher) CheckLoginStatus() bool {
currentURL := p.GetCurrentURL()
if strings.Contains(currentURL, p.LoginedURL) {

View File

@ -20,25 +20,6 @@ func NewJianshuPublisher(ctx context.Context, task *TaskParams, cfg *config.Conf
return &JianshuPublisher{NewBasePublisher(ctx, task, cfg, logger)}
}
func (p *JianshuPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Close()
p.Page.MustNavigate(p.EditorURL)
p.Sleep(3)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *JianshuPublisher) CheckLoginStatus() bool {
currentURL := p.GetCurrentURL()
if strings.Contains(currentURL, p.LoginURL) {

View File

@ -20,25 +20,6 @@ func NewSohuPublisher(ctx context.Context, task *TaskParams, cfg *config.Config,
return &SohuPublisher{NewBasePublisher(ctx, task, cfg, logger)}
}
func (p *SohuPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Close()
p.Page.MustNavigate(p.EditorURL)
p.Sleep(3)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *SohuPublisher) CheckLoginStatus() bool {
currentURL := p.GetCurrentURL()
if strings.Contains(currentURL, p.LoginURL) {

View File

@ -23,25 +23,6 @@ func NewShipinhaoVideoPublisher(ctx context.Context, task *TaskParams, config *c
}
}
func (p *ShipinhaoVideoPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Close()
p.Page.MustNavigate(p.LoginedURL)
p.Sleep(3)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *ShipinhaoVideoPublisher) CheckLoginStatus() bool {
currentURL := p.GetCurrentURL()
if strings.Contains(currentURL, "login") || strings.Contains(currentURL, "passport") {

View File

@ -21,25 +21,6 @@ func NewToutiaoPublisher(ctx context.Context, task *TaskParams, cfg *config.Conf
return &ToutiaoPublisher{NewBasePublisher(ctx, task, cfg, logger)}
}
func (p *ToutiaoPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Close()
p.Page.MustNavigate(p.EditorURL)
p.Sleep(2)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *ToutiaoPublisher) CheckLoginStatus() bool {
currentURL := p.GetCurrentURL()
// 如果在登录页面,未登录

View File

@ -28,25 +28,6 @@ func NewWangyiPublisher(ctx context.Context, task *TaskParams, cfg *config.Confi
}
}
func (p *WangyiPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Page.Close()
p.Page.MustNavigate(p.EditorURL)
p.Sleep(3)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *WangyiPublisher) CheckLoginStatus() bool {
currentURL := p.GetCurrentURL()

View File

@ -21,25 +21,6 @@ func NewXiaohongshuPublisher(ctx context.Context, task *TaskParams, cfg *config.
return &XiaohongshuPublisher{NewBasePublisher(ctx, task, cfg, logger)}
}
func (p *XiaohongshuPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Close()
p.Page.MustNavigate(p.LoginedURL)
p.Sleep(3)
//p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *XiaohongshuPublisher) WaitLogin() (bool, string) {
p.LogInfo("开始等待登录...")

View File

@ -29,25 +29,6 @@ func NewXiaohongshuVideoPublisher(ctx context.Context, task *TaskParams, cfg *co
}
}
func (p *XiaohongshuVideoPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Page.Close()
p.Page.MustNavigate(p.LoginedURL)
p.Sleep(3)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *XiaohongshuVideoPublisher) WaitLogin() (bool, string) {
p.LogInfo("开始等待登录...")

View File

@ -20,25 +20,6 @@ func NewZhihuPublisher(ctx context.Context, task *TaskParams, cfg *config.Config
return &ZhihuPublisher{NewBasePublisher(ctx, task, cfg, logger)}
}
func (p *ZhihuPublisher) CheckLogin() (bool, string) {
p.LogInfo("检查登录状态...")
if err := p.SetupDriver(); err != nil {
return false, fmt.Sprintf("浏览器启动失败: %v", err)
}
defer p.Page.Close()
p.Page.MustNavigate(p.EditorURL)
p.Sleep(3)
p.WaitForPageReady(5)
if p.CheckLoginStatus() {
p.SaveCookies()
return true, "已登录"
}
return false, "未登录"
}
func (p *ZhihuPublisher) CheckLoginStatus() bool {
currentURL := p.GetCurrentURL()
if strings.Contains(currentURL, p.LoginURL) {

View File

@ -53,12 +53,17 @@ func (m *AppModule) Register(router fiber.Router) {
router.Post("/product/add", vali(m.productService.Add, &entitys.CreateProductRequest{}))
router.Post("/product/list", vali(m.productService.List, &entitys.ProductListRequest{}))
router.Post("/product/detail", vali(m.productService.Detail, &entitys.ProductDetailRequest{}))
router.Post("/product/update", vali(m.productService.Update, &entitys.ProductUpdateRequest{}))
router.Post("/product/del", vali(m.productService.Del, &entitys.ProductDelRequest{}))
router.Post("/img/upload", m.productService.ImgUpload)
router.Post("/plat/list", vali(m.appService.PlatList, &entitys.PlatListRequest{}))
router.Get("/product/word/article_type_list", m.productSourceService.ArticalTypeList)
router.Post("/product/word/create", vali(m.productSourceService.Create, &entitys.ProductSourceCreateRequest{}))
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{}))
router.Post("/product/source/publish", vali(m.productSourceService.Publish, &entitys.ProductPublishRequest{}))
}

View File

@ -2,7 +2,6 @@ package service
import (
"fmt"
"github.com/gofiber/fiber/v2"
"xorm.io/builder"
@ -191,3 +190,21 @@ func (a *AppService) GetApp(c *fiber.Ctx, req *entitys.GetAppRequest) error {
return pkg.HandleResponse(c, result)
}
func (a *AppService) PlatList(c *fiber.Ctx, req *entitys.PlatListRequest) error {
_, err := a.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken)
if err != nil {
return err
}
cond := builder.NewCond().
And(builder.Eq{"status": 1})
if req.PlatType != 0 {
cond.And(builder.Eq{"plat_type": req.PlatType})
}
list, err := a.platImpl.GetRange(c.UserContext(), &cond)
if err != nil {
return err
}
return pkg.HandleResponse(c, list)
}

View File

@ -8,9 +8,10 @@ import (
"geo/internal/entitys"
"geo/pkg"
"geo/tmpl/dataTemp"
"geo/tmpl/errcode"
"github.com/go-viper/mapstructure/v2"
"github.com/gofiber/fiber/v2"
"io"
"xorm.io/builder"
)
@ -18,13 +19,15 @@ type ProductService struct {
cfg *config.Config
productImpl *impl.ProductImpl
authBiz *biz.AuthBiz
productBiz *biz.ProductBiz
}
func NewProductService(cfg *config.Config, ProductImpl *impl.ProductImpl, authBiz *biz.AuthBiz) *ProductService {
func NewProductService(cfg *config.Config, ProductImpl *impl.ProductImpl, authBiz *biz.AuthBiz, productBiz *biz.ProductBiz) *ProductService {
return &ProductService{
cfg: cfg,
productImpl: ProductImpl,
authBiz: authBiz,
productBiz: productBiz,
}
}
@ -45,6 +48,21 @@ func (p *ProductService) Add(c *fiber.Ctx, req *entitys.CreateProductRequest) er
return nil
}
func (p *ProductService) Detail(c *fiber.Ctx, req *entitys.ProductDetailRequest) error {
_, err := p.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken)
if err != nil {
return err
}
var detail model.Product
cond := builder.NewCond().
And(builder.Eq{"id": req.Id})
err = p.productImpl.GetOneBySearchStruct(c.UserContext(), &cond, &detail)
if err != nil {
return err
}
return pkg.HandleResponse(c, detail)
}
func (p *ProductService) List(c *fiber.Ctx, req *entitys.ProductListRequest) error {
_, _, err := p.authBiz.UserAndTokenValid(c.UserContext(), req.UserIndex, req.AccessToken)
if err != nil {
@ -95,3 +113,42 @@ func (p *ProductService) Del(c *fiber.Ctx, req *entitys.ProductDelRequest) error
err = p.productImpl.DeleteByKey(c.UserContext(), p.productImpl.PrimaryKey(), req.Id)
return nil
}
func (p *ProductService) ImgUpload(c *fiber.Ctx) error {
access := c.FormValue("access_token", "")
if access == "" {
return errcode.ParamErr("access_token未找到")
}
// 验证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())
}
// 获取文件扩展名
ext := pkg.GetFileExtension(fileHeader.Filename)
// 根据图片类型生成文件名
fileName := pkg.GenerateImageFileName(ext)
url, err := p.productBiz.SourceUpload(c.UserContext(), fileBytes, fileName)
if err != nil {
return err
}
return pkg.HandleResponse(c, fiber.Map{"url": url})
}

View File

@ -25,12 +25,23 @@ type ProductSourceService struct {
cfg *config.Config
productImpl *impl.ProductImpl
productSourceImpl *impl.ProductSourceImpl
articleTypeImpl *impl.ArticleTypeImpl
authBiz *biz.AuthBiz
aiBiz *biz.AiBiz
productBiz *biz.ProductBiz
publishBiz *biz.PublishBiz
}
func NewProductSourceService(cfg *config.Config, ProductImpl *impl.ProductImpl, authBiz *biz.AuthBiz, aiBiz *biz.AiBiz, productBiz *biz.ProductBiz, productSource *impl.ProductSourceImpl) *ProductSourceService {
func NewProductSourceService(
cfg *config.Config,
ProductImpl *impl.ProductImpl,
authBiz *biz.AuthBiz,
aiBiz *biz.AiBiz,
productBiz *biz.ProductBiz,
productSource *impl.ProductSourceImpl,
publishBiz *biz.PublishBiz,
articleTypeImpl *impl.ArticleTypeImpl,
) *ProductSourceService {
return &ProductSourceService{
cfg: cfg,
productImpl: ProductImpl,
@ -38,6 +49,8 @@ func NewProductSourceService(cfg *config.Config, ProductImpl *impl.ProductImpl,
aiBiz: aiBiz,
productBiz: productBiz,
productSourceImpl: productSource,
publishBiz: publishBiz,
articleTypeImpl: articleTypeImpl,
}
}
@ -175,11 +188,11 @@ func (p *ProductSourceService) Update(c *fiber.Ctx, req *entitys.ProductSourceUp
return err
}
var update = &model.ProductSource{}
if req.Title != "" {
update.Title = req.Title
if req.Title != nil {
update.Title = *req.Title
}
if len(req.Tag) > 0 {
update.Tag = strings.Join(req.Tag, ",")
if req.Tag != nil {
update.Tag = strings.Join(*req.Tag, ",")
}
return p.productBiz.UpdateSourceById(c.UserContext(), req.SourceId, update)
@ -194,3 +207,63 @@ func (p *ProductSourceService) Del(c *fiber.Ctx, req *entitys.ProductSourceDelRe
return p.productBiz.DelSourceById(c.UserContext(), req.SourceId)
}
func (p *ProductSourceService) Publish(c *fiber.Ctx, req *entitys.ProductPublishRequest) error {
// 验证token
tokenInfo, err := p.authBiz.ValidateAccessToken(c.UserContext(), req.AccessToken)
if err != nil {
return err
}
var source model.ProductSource
err = p.productSourceImpl.GetByKey(c.UserContext(), p.productSourceImpl.PrimaryKey(), req.SourceId, &source)
if err != nil {
return err
}
var product model.Product
err = p.productImpl.GetByKey(c.UserContext(), p.productImpl.PrimaryKey(), source.ProductId, &product)
if err != nil {
return err
}
if product.Imgs == "" {
return errcode.NotFound("请先上传产品图片")
}
ptime, err := time.Parse(time.DateTime, req.PublishTime)
if err != nil {
return errcode.ParamErr("发布时间格式错误")
}
var validRecords = make([]*model.Publish, 0, len(req.Plat))
for _, v := range req.Plat {
validRecords = append(validRecords, &model.Publish{
UserIndex: product.UserIndex,
SourceID: req.SourceId,
RequestID: fmt.Sprintf("%s_%d", pkg.GenerateRandomLowerString(3), time.Now().UnixNano()),
Title: source.Title,
Tag: source.Tag,
Type: source.SourceType,
PlatIndex: v,
URL: source.SourceURL,
PublishTime: ptime,
Img: strings.Split(product.Imgs, ",")[0],
TokenID: tokenInfo.ID,
})
}
err = p.publishBiz.BatchInsertPublish(c.UserContext(), validRecords)
if err != nil {
return err
}
return pkg.HandleResponse(c, fiber.Map{
"total": len(validRecords),
})
}
func (p *ProductSourceService) ArticalTypeList(c *fiber.Ctx) error {
cond := builder.NewCond().
And(builder.Eq{"status": 1})
list, err := p.articleTypeImpl.GetRange(c.UserContext(), &cond)
if err != nil {
return err
}
return pkg.HandleResponse(c, list)
}

View File

@ -353,3 +353,20 @@ func ReadDocxToBytes(filePath string) ([]byte, error) {
return data, nil
}
// getFileExtension 获取文件扩展名
func GetFileExtension(filename string) string {
parts := strings.Split(filename, ".")
if len(parts) < 2 {
return ".jpg" // 默认扩展名
}
return "." + parts[len(parts)-1]
}
// generateImageFileName 根据图片类型生成文件名
func GenerateImageFileName(ext string) string {
// 生成唯一标识
timestamp := time.Now().UnixNano() / 1e6 // 毫秒级时间戳
randomNum := GenerateRandomLowerString(3) // 随机数
return fmt.Sprintf("img_%d%s%s", timestamp, randomNum, ext)
}

41
pkg/rand.go Normal file
View File

@ -0,0 +1,41 @@
package pkg
import (
"math/rand/v2"
)
// GenerateRandomStringCustom 使用自定义字符集
func GenerateRandomStringCustom(n int, charset string) string {
result := make([]byte, n)
for i := range result {
result[i] = charset[rand.IntN(len(charset))]
}
return string(result)
}
// 纯数字字符串
func GenerateRandomNumberString(n int) string {
return GenerateRandomStringCustom(n, "0123456789")
}
// 纯小写字母
func GenerateRandomLowerString(n int) string {
return GenerateRandomStringCustom(n, "abcdefghijklmnopqrstuvwxyz")
}
// 纯大写字母
func GenerateRandomUpperString(n int) string {
return GenerateRandomStringCustom(n, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
}
// 使用自定义随机源(例如固定种子)
func GenerateRandomStringWithSeed(n int, seed uint64) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
rng := rand.New(rand.NewPCG(seed, seed))
result := make([]byte, n)
for i := range result {
result[i] = charset[rng.IntN(len(charset))]
}
return string(result)
}

View File

@ -36,3 +36,22 @@ func SuccessWithPageMsg(c *fiber.Ctx, list interface{}, total int64, page, pageS
}
return HandleResponse(c, response)
}
func HandleResponseWithErr(c *fiber.Ctx, data interface{}, errs error) (err error) {
if errs != nil {
err = errs
return
}
switch data.(type) {
case error:
err = data.(error)
case int, int32, int64, float32, float64, string, bool:
c.Response().SetBody([]byte(fmt.Sprintf("%s", data)))
case []byte:
c.Response().SetBody(data.([]byte))
default:
dataByte, _ := json.Marshal(data)
c.Response().SetBody(dataByte)
}
return
}