Skip to content

feat: red envelope cover#182

Merged
chenyme merged 16 commits intolinux-do:masterfrom
small-lovely-cat:feat-red-envelope-cover
Feb 16, 2026
Merged

feat: red envelope cover#182
chenyme merged 16 commits intolinux-do:masterfrom
small-lovely-cat:feat-red-envelope-cover

Conversation

@small-lovely-cat
Copy link
Contributor

@small-lovely-cat small-lovely-cat commented Jan 27, 2026

例行检查

  • 我已阅读并理解 贡献者公约
  • 我已阅读并同意 贡献者许可协议 (CLA),确认我的贡献将根据项目的 Apache2.0 许可证进行许可
  • 我知晓如果此 PR 并不做出实质性更改,或可被认为是为了PR被合并而提交PR的,则可能不会被合并

变更内容

红包功能扩展,添加红包封面及异形红包设置。

image image

新增功能

由于考虑到上传图片和引用是分开的操作,为避免服务器被当作图床使用,对于红包未引用的图片,添加了对应的cron计划任务,这些图片会在2h后自动删除。

变更原因

红包功能完善。

@chenyme chenyme requested review from chenyme and yyg-max and removed request for yyg-max February 8, 2026 05:15
@chenyme
Copy link
Collaborator

chenyme commented Feb 8, 2026

image

上传文件过大时,toast 会一直卡在加载中

ps: 要是封面需要花 LDC 上传会更好~ 或者学习wx,发n个红包,单个封面收费 * n 哈哈哈
再ps: 封面我觉得可以官方来出皮肤进行购买,然后自定义的需要收费?或者和wx封面一样,可以发给别人?(有点太复杂了哈哈哈)

@small-lovely-cat
Copy link
Contributor Author

small-lovely-cat commented Feb 8, 2026

image 上传文件过大时,toast 会一直卡在加载中

ps: 要是封面需要花 LDC 上传会更好~ 或者学习wx,发n个红包,单个封面收费 * n 哈哈哈 再ps: 封面我觉得可以官方来出皮肤进行购买,然后自定义的需要收费?或者和wx封面一样,可以发给别人?(有点太复杂了哈哈哈)

@chenyme

上传猫猫记得加了大小限制欸,不知道出了什么bug了,后面猫猫再去看看 w

花LDC买封面等后面再加吧,就当明年的功能,今年就不加了,先试试看整套流程能不能跑通 owo

或许还可以加个封面广场,先看看今年大家上传的封面有什么创意,然后挑选几张好看的作为预设的红包封面 w

还有一点思路猫猫记录一下,discourse的onebox也可以利用一下,就不要单纯显示链接了,像wx红包一样显示个icon,这样更有氛围 w

TDL 有点长了,反正后面一点点做吧 w


知道问题了,当初ESLint修any的时候把content-type丢掉了,结果默认传了json www

Copy link
Collaborator

@yyg-max yyg-max left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. internal/db/migrator/migrator.go 里加个迁移
  2. 常量定义放到 constants.go
  3. 上传或删除失败使用事务别导致数据混乱了

UploadRedEnvelopeCover 感觉写的有点复杂,你先看看能不能再优化 😘

Greeting string `json:"greeting" gorm:"size:100"`
Status RedEnvelopeStatus `json:"status" gorm:"type:varchar(20);not null"`
CoverImage string `json:"cover_image,omitempty" gorm:"size:500"`
HeterotypicImage string `json:"heterotypic_image,omitempty" gorm:"size:500"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

关联Upload比较好点

}

// validateImageURL 验证图片URL的安全性
func validateImageURL(url string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateImageURL方法 可以放到 utils里或 logic验证能重复利用放 internal/util 下,routers 只放接口。

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uploads/redenvelope 使用常量里面的,重复的可以定义到公共常量里

@small-lovely-cat
Copy link
Contributor Author

small-lovely-cat commented Feb 8, 2026

@yyg-max 改好了,麻烦佬再看看 owo

现在改成了上传文件时会同时返回URL和ID,前端发红包时只传图片的文件ID,也就不需要validateImageURL这个函数了。

其他的都修改了一下,现在红包表里面只存放ID了,具体URL到Upload表里面找 www

for _, upload := range unusedUploads {
totalProcessed++

tx := db.DB(ctx).Begin()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

用 if err := db.DB(c.Request.Context()).Transaction(func(tx *gorm.DB) error {} 吧,比较清晰点

MimeType string `json:"mime_type" gorm:"size:100;not null"` // MIME类型
Purpose string `json:"purpose" gorm:"size:50;not null;index"` // 用途 (red_envelope_cover, red_envelope_heterotypic)
Status UploadStatus `json:"status" gorm:"type:varchar(20);not null;index"` // 状态
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;index"`
Copy link
Collaborator

@yyg-max yyg-max Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

使用了主键索引 Status CreatedAt 索引先不加

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

还有这个

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个也改好了 w

filename := fmt.Sprintf("%d_%s_%s_%d%s", currentUser.ID, coverType, md5Sum[:8], timestamp, safeExt)

// 创建上传目录 (使用更安全的权限)
uploadPath := filepath.Join(UploadDir, time.Now().Format("2006/01"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

精细到日是不是更好点

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个呢

Copy link
Contributor Author

@small-lovely-cat small-lovely-cat Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个改成年月日了,上面那个猫猫明天再看看 owo

Copy link
Collaborator

@yyg-max yyg-max left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你理解错了(可能描述也比较抽象),当前版本你先回退吧。 放到 internal/apps/admin/task/routers.go 这个你先不改吧,把其他的改好就行

@small-lovely-cat
Copy link
Contributor Author

small-lovely-cat commented Feb 9, 2026

你理解错了(可能描述也比较抽象),当前版本你先回退吧。 放到 internal/apps/admin/task/routers.go 这个你先不改吧,把其他的改好就行

okok w

猫猫想了一下,是不是意思是需要在/admin/task/routers.go,DispatchTask里面允许手动触发这个清理任务?猫猫加一下 owo

@yyg-max

@yyg-max
Copy link
Collaborator

yyg-max commented Feb 9, 2026

你理解错了(可能描述也比较抽象),当前版本你先回退吧。 放到 internal/apps/admin/task/routers.go 这个你先不改吧,把其他的改好就行

okok w

猫猫想了一下,是不是意思是需要在/admin/task/routers.go,DispatchTask里面允许手动触发这个清理任务?猫猫加一下 owo

@yyg-max

是的,只需要加个任务标识就行了

@small-lovely-cat
Copy link
Contributor Author

small-lovely-cat commented Feb 9, 2026

你理解错了(可能描述也比较抽象),当前版本你先回退吧。 放到 internal/apps/admin/task/routers.go 这个你先不改吧,把其他的改好就行

okok w
猫猫想了一下,是不是意思是需要在/admin/task/routers.go,DispatchTask里面允许手动触发这个清理任务?猫猫加一下 owo
@yyg-max

是的,只需要加个任务标识就行了

加好了 www
在上面一个commit里面一起推上来了 www

y佬再看看 owo

@yyg-max
Copy link
Collaborator

yyg-max commented Feb 9, 2026

任务没啥问题,再看看上面的问题

@small-lovely-cat
Copy link
Contributor Author

@yyg-max

改好了应该,你看看还有没有缺少的 w

…for cropping and an updated image upload interface.
FileSize int64 `json:"file_size" gorm:"not null"` // 文件大小(字节)
MimeType string `json:"mime_type" gorm:"size:100;not null"` // MIME类型
Purpose string `json:"purpose" gorm:"size:50;not null;index"` // 用途 (red_envelope_cover, red_envelope_heterotypic)
Status UploadStatus `json:"status" gorm:"type:varchar(20);not null"` // 状态
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个状态没有必要使用,删除使用单独的DeletedAt字段覆盖


const (
// 上传目录
UploadDir = "uploads/redenvelope"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upload 如果是一个通用的模块,上传目录为什么带有 redenvelope 这个路径

UploadDir = "uploads/redenvelope"

// 允许的图片类型
AllowedImageTypes = "image/jpeg,image/png,image/jpg,image/webp"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修改为配置项

MaxFileSize = 2 * 1024 * 1024

// 最大图片尺寸
MaxImageWidth = 4096
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

尺寸可以不限制?

ID uint64 `json:"id,string"`
URL string `json:"url"`
Filename string `json:"filename,omitempty"`
Size int64 `json:"size,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response需要提供size width height吗?

currentUser, _ := util.GetFromContext[*model.User](c, oauth.UserObjKey)

coverType := c.Query("type")
purpose, ok := purposeMap[coverType]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么要转换

return
}

// 按 file_url 去重
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uniq 就不用去重了


response := UploadResponse{
ID: recordID,
URL: urlPath,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

urlPath 里面要暴露真实的存储路径吗

)

// HandleCleanupUnusedUploads 处理清理未使用上传文件的定时任务
func HandleCleanupUnusedUploads(ctx context.Context, t *asynq.Task) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

意义何在

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个前面说了,因为上传和发红包是两个逻辑,如果上传了被当作图床了就不好了 www

// RedEnvelopeView 红包视图(包含上传URL)
type RedEnvelopeView struct {
model.RedEnvelope
CoverImageURL string `json:"cover_image_url,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不建议后端来拼接,应该是后端提供一个 common api,比如 /f/:id,前端拿到 id直接访问

@chenyme chenyme merged commit d5e622f into linux-do:master Feb 16, 2026
9 checks passed
@small-lovely-cat small-lovely-cat deleted the feat-red-envelope-cover branch February 16, 2026 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants