优化删除
This commit is contained in:
@@ -44,6 +44,23 @@ func (h *Handler) BatchCreateQrCodeHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"data": qrcodes})
|
||||
}
|
||||
|
||||
// BatchDeleteQrCodeHandler 批量删除二维码
|
||||
func (h *Handler) BatchDeleteQrCodeHandler(c *gin.Context) {
|
||||
var req BatchDeleteQrCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
deleted, err := h.service.repo.BatchDeleteQrCodes(c.Request.Context(), req.IDs)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": gin.H{"deleted": deleted}})
|
||||
}
|
||||
|
||||
// ListQrCodesHandler 列表查询二维码
|
||||
func (h *Handler) ListQrCodesHandler(c *gin.Context) {
|
||||
var req QrCodeListRequest
|
||||
@@ -157,10 +174,6 @@ func (h *Handler) DeleteQrCodeHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "二维码不存在"})
|
||||
return
|
||||
}
|
||||
if err == ErrQrCodeCannotDelete {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "已使用的二维码不能删除"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ const (
|
||||
|
||||
var (
|
||||
ErrQrCodeNotFound = errors.New("二维码不存在")
|
||||
ErrQrCodeCannotDelete = errors.New("已使用的二维码不能删除")
|
||||
ErrQrCodeCannotReenable = errors.New("已发放的二维码不能改回待用")
|
||||
)
|
||||
|
||||
@@ -43,6 +42,11 @@ type BatchCreateQrCodeRequest struct {
|
||||
Items []CreateQrCodeRequest `json:"items" binding:"required,min=1,max=20"`
|
||||
}
|
||||
|
||||
// BatchDeleteQrCodeRequest 批量删除二维码请求
|
||||
type BatchDeleteQrCodeRequest struct {
|
||||
IDs []uint64 `json:"ids" binding:"required,min=1,max=100"`
|
||||
}
|
||||
|
||||
// UpdateQrCodeRequest 更新二维码请求
|
||||
type UpdateQrCodeRequest struct {
|
||||
ImageURL *string `json:"image_url"`
|
||||
@@ -313,7 +317,7 @@ func qrcodeWasIssued(qrcode model.ChatQrCode) bool {
|
||||
return qrcode.Status == QrCodeStatusUsed || qrcode.ConversationID != nil || qrcode.UsedAt != nil
|
||||
}
|
||||
|
||||
// DeleteQrCode 删除二维码(仅未使用的可删除)
|
||||
// DeleteQrCode 删除二维码
|
||||
func (r *Repository) DeleteQrCode(ctx context.Context, id uint64) error {
|
||||
var qrcode model.ChatQrCode
|
||||
if err := r.db.WithContext(ctx).First(&qrcode, id).Error; err != nil {
|
||||
@@ -323,12 +327,20 @@ func (r *Repository) DeleteQrCode(ctx context.Context, id uint64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 已使用的不能删除
|
||||
if qrcode.Status == QrCodeStatusUsed {
|
||||
return ErrQrCodeCannotDelete
|
||||
return r.db.WithContext(ctx).Delete(&qrcode).Error
|
||||
}
|
||||
|
||||
// BatchDeleteQrCodes 批量删除二维码
|
||||
func (r *Repository) BatchDeleteQrCodes(ctx context.Context, ids []uint64) (int64, error) {
|
||||
if len(ids) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return r.db.WithContext(ctx).Delete(&qrcode).Error
|
||||
result := r.db.WithContext(ctx).Where("id IN ?", ids).Delete(&model.ChatQrCode{})
|
||||
if result.Error != nil {
|
||||
return 0, result.Error
|
||||
}
|
||||
return result.RowsAffected, nil
|
||||
}
|
||||
|
||||
// fetchUnusedQrCode 获取一个未使用且未过期的二维码(带行锁)
|
||||
|
||||
@@ -231,6 +231,80 @@ func TestUpdateQrCodeGroupNameAndRenameFlag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteQrCodeAllowsIssuedQrCode(t *testing.T) {
|
||||
db := setupQrCodeTestDB(t)
|
||||
repo := NewRepository(db, nil)
|
||||
now := time.Date(2026, 6, 19, 17, 8, 0, 0, time.UTC)
|
||||
conversationID := uint64(1003)
|
||||
qrcode := model.ChatQrCode{
|
||||
ImageURL: "/api/files/object?key=qrcode/issued.png",
|
||||
Status: QrCodeStatusUsed,
|
||||
ConversationID: &conversationID,
|
||||
UsedAt: &now,
|
||||
CreatedBy: 1,
|
||||
}
|
||||
if err := db.Create(&qrcode).Error; err != nil {
|
||||
t.Fatalf("创建二维码失败: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.DeleteQrCode(t.Context(), qrcode.ID); err != nil {
|
||||
t.Fatalf("删除已发放二维码失败: %v", err)
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&model.ChatQrCode{}).Where("id = ?", qrcode.ID).Count(&count).Error; err != nil {
|
||||
t.Fatalf("统计二维码失败: %v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatalf("二维码数量 = %d, want 0", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatchDeleteQrCodes(t *testing.T) {
|
||||
db := setupQrCodeTestDB(t)
|
||||
repo := NewRepository(db, nil)
|
||||
now := time.Date(2026, 6, 19, 17, 8, 0, 0, time.UTC)
|
||||
conversationID := uint64(1004)
|
||||
qrcodes := []model.ChatQrCode{
|
||||
{
|
||||
ImageURL: "/api/files/object?key=qrcode/unused.png",
|
||||
Status: QrCodeStatusUnused,
|
||||
CreatedBy: 1,
|
||||
},
|
||||
{
|
||||
ImageURL: "/api/files/object?key=qrcode/used.png",
|
||||
Status: QrCodeStatusUsed,
|
||||
ConversationID: &conversationID,
|
||||
UsedAt: &now,
|
||||
CreatedBy: 1,
|
||||
},
|
||||
{
|
||||
ImageURL: "/api/files/object?key=qrcode/keep.png",
|
||||
Status: QrCodeStatusDisabled,
|
||||
CreatedBy: 1,
|
||||
},
|
||||
}
|
||||
if err := db.Create(&qrcodes).Error; err != nil {
|
||||
t.Fatalf("创建二维码失败: %v", err)
|
||||
}
|
||||
|
||||
deleted, err := repo.BatchDeleteQrCodes(t.Context(), []uint64{qrcodes[0].ID, qrcodes[1].ID})
|
||||
if err != nil {
|
||||
t.Fatalf("批量删除二维码失败: %v", err)
|
||||
}
|
||||
if deleted != 2 {
|
||||
t.Fatalf("删除数量 = %d, want 2", deleted)
|
||||
}
|
||||
|
||||
var remaining []model.ChatQrCode
|
||||
if err := db.Order("id ASC").Find(&remaining).Error; err != nil {
|
||||
t.Fatalf("查询剩余二维码失败: %v", err)
|
||||
}
|
||||
if len(remaining) != 1 || remaining[0].ID != qrcodes[2].ID {
|
||||
t.Fatalf("剩余二维码 = %+v, want id %d", remaining, qrcodes[2].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureListingConversationCreatesQrCodeDeliveryTaskWhenStockEmpty(t *testing.T) {
|
||||
db := setupQrCodeTestDB(t)
|
||||
listing := model.RentalListing{
|
||||
|
||||
@@ -593,6 +593,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
|
||||
// 二维码池管理
|
||||
adminRoutes.POST("/chats/qrcodes", requirePerm("chat:manage"), chatHandler.CreateQrCodeHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/batch", requirePerm("chat:manage"), chatHandler.BatchCreateQrCodeHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/batch-delete", requirePerm("chat:manage"), chatHandler.BatchDeleteQrCodeHandler)
|
||||
adminRoutes.GET("/chats/qrcodes", requirePerm("chat:view"), chatHandler.ListQrCodesHandler)
|
||||
adminRoutes.GET("/chats/qrcodes/stats", requirePerm("chat:view"), chatHandler.GetQrCodeStatsHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/ocr-group-name", requirePerm("chat:manage"), chatHandler.RecognizeQrCodeGroupNameHandler)
|
||||
|
||||
Reference in New Issue
Block a user