chore(init): 初始化解忧商行项目并完成 Phase 0 基座
This commit is contained in:
@@ -0,0 +1,425 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"hfb_sys/backend/pkg/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CreateQrCodeHandler 创建二维码
|
||||
func (h *Handler) CreateQrCodeHandler(c *gin.Context) {
|
||||
var req CreateQrCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
adminID := c.GetUint64("admin_id")
|
||||
qrcode, err := h.service.repo.CreateQrCode(c.Request.Context(), adminID, req)
|
||||
if err != nil {
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": qrcode})
|
||||
}
|
||||
|
||||
// BatchCreateQrCodeHandler 批量创建二维码
|
||||
func (h *Handler) BatchCreateQrCodeHandler(c *gin.Context) {
|
||||
var req BatchCreateQrCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
adminID := c.GetUint64("admin_id")
|
||||
qrcodes, err := h.service.repo.BatchCreateQrCode(c.Request.Context(), adminID, req)
|
||||
if err != nil {
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": gin.H{"deleted": deleted}})
|
||||
}
|
||||
|
||||
// ListQrCodesHandler 列表查询二维码
|
||||
func (h *Handler) ListQrCodesHandler(c *gin.Context) {
|
||||
var req QrCodeListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
qrcodes, total, err := h.service.repo.ListQrCodes(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": PaginatedResult{
|
||||
Items: qrcodes,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
PageSize: req.Limit,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetQrCodeStatsHandler 获取二维码统计
|
||||
func (h *Handler) GetQrCodeStatsHandler(c *gin.Context) {
|
||||
stats, err := h.service.repo.GetQrCodeStats(c.Request.Context())
|
||||
if err != nil {
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": stats})
|
||||
}
|
||||
|
||||
// RecognizeQrCodeGroupNameHandler 同步调用 PaddleOCR API 识别二维码图片中的企业微信群名(保留向后兼容)
|
||||
func (h *Handler) RecognizeQrCodeGroupNameHandler(c *gin.Context) {
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请上传二维码图片"})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
result, err := h.service.repo.RecognizeQrCodeGroupName(
|
||||
c.Request.Context(),
|
||||
header.Filename,
|
||||
header.Header.Get("Content-Type"),
|
||||
file,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrQrCodeOCRNotConfigured) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请先在系统配置中填写 PaddleOCR API Token"})
|
||||
return
|
||||
}
|
||||
if errors.Is(err, ErrQrCodeOCRInvalidFile) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的二维码图片"})
|
||||
return
|
||||
}
|
||||
if errors.Is(err, ErrQrCodeOCRUnavailable) {
|
||||
response.RecordError(c, err)
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "PaddleOCR 服务暂时不可用"})
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": result})
|
||||
}
|
||||
|
||||
// SubmitOCRJobHandler 提交 OCR 任务到 PaddleOCR,立即返回任务 ID
|
||||
func (h *Handler) SubmitOCRJobHandler(c *gin.Context) {
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请上传二维码图片"})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
jobID, err := h.service.repo.SubmitOCRJob(
|
||||
c.Request.Context(),
|
||||
header.Filename,
|
||||
header.Header.Get("Content-Type"),
|
||||
file,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrQrCodeOCRNotConfigured) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请先在系统配置中填写 PaddleOCR API Token"})
|
||||
return
|
||||
}
|
||||
if errors.Is(err, ErrQrCodeOCRInvalidFile) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的二维码图片"})
|
||||
return
|
||||
}
|
||||
if errors.Is(err, ErrQrCodeOCRUnavailable) {
|
||||
response.RecordError(c, err)
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "PaddleOCR 服务暂时不可用"})
|
||||
return
|
||||
}
|
||||
if errors.Is(err, ErrQrCodeOCRRedisDisabled) {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "OCR 异步模式需要 Redis"})
|
||||
return
|
||||
}
|
||||
if errors.Is(err, ErrQrCodeOCRJobNotFound) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "OCR 任务不存在或已过期"})
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": gin.H{"job_id": jobID}})
|
||||
}
|
||||
|
||||
// GetOCRJobResultHandler 查询异步 OCR 任务状态和结果
|
||||
func (h *Handler) GetOCRJobResultHandler(c *gin.Context) {
|
||||
jobID := c.Param("jobId")
|
||||
if jobID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "缺少任务 ID"})
|
||||
return
|
||||
}
|
||||
|
||||
record, err := h.service.repo.PollOCRJobResult(c.Request.Context(), jobID)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrQrCodeOCRJobNotFound) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "OCR 任务不存在或已过期"})
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": record})
|
||||
}
|
||||
|
||||
// UpdateQrCodeHandler 更新二维码
|
||||
func (h *Handler) UpdateQrCodeHandler(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateQrCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.repo.UpdateQrCode(c.Request.Context(), id, req); err != nil {
|
||||
if err == ErrQrCodeNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "二维码不存在"})
|
||||
return
|
||||
}
|
||||
if err == ErrQrCodeCannotReenable {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "已发放的二维码不能改回待用"})
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "更新成功"})
|
||||
}
|
||||
|
||||
// DeleteQrCodeHandler 删除二维码
|
||||
func (h *Handler) DeleteQrCodeHandler(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的ID"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.repo.DeleteQrCode(c.Request.Context(), id); err != nil {
|
||||
if err == ErrQrCodeNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "二维码不存在"})
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
|
||||
}
|
||||
|
||||
func (h *Handler) siteIDParam(c *gin.Context) (uint64, bool) {
|
||||
siteID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil || siteID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的分站"})
|
||||
return 0, false
|
||||
}
|
||||
return siteID, true
|
||||
}
|
||||
|
||||
func writeSiteQrCodeError(c *gin.Context, err error) bool {
|
||||
switch {
|
||||
case errors.Is(err, ErrQrCodeSiteNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "分销站点不存在"})
|
||||
case errors.Is(err, ErrQrCodeNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "二维码不存在"})
|
||||
case errors.Is(err, ErrQrCodeCannotReenable):
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "已发放的二维码不能改回待用"})
|
||||
default:
|
||||
if err != nil && err.Error() == "无效的状态值" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *Handler) ListSiteQrCodesHandler(c *gin.Context) {
|
||||
siteID, ok := h.siteIDParam(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req QrCodeListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
qrcodes, total, err := h.service.repo.ListSiteQrCodes(c.Request.Context(), siteID, req)
|
||||
if err != nil {
|
||||
if writeSiteQrCodeError(c, err) {
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": PaginatedResult{
|
||||
Items: qrcodes,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
PageSize: req.Limit,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) GetSiteQrCodeStatsHandler(c *gin.Context) {
|
||||
siteID, ok := h.siteIDParam(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
stats, err := h.service.repo.GetSiteQrCodeStats(c.Request.Context(), siteID)
|
||||
if err != nil {
|
||||
if writeSiteQrCodeError(c, err) {
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": stats})
|
||||
}
|
||||
|
||||
func (h *Handler) BatchCreateSiteQrCodeHandler(c *gin.Context) {
|
||||
siteID, ok := h.siteIDParam(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req BatchCreateQrCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
adminID := c.GetUint64("admin_id")
|
||||
qrcodes, err := h.service.repo.BatchCreateSiteQrCode(c.Request.Context(), adminID, siteID, req)
|
||||
if err != nil {
|
||||
if writeSiteQrCodeError(c, err) {
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": qrcodes})
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateSiteQrCodeHandler(c *gin.Context) {
|
||||
siteID, ok := h.siteIDParam(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseUint(c.Param("qrcodeId"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的ID"})
|
||||
return
|
||||
}
|
||||
var req UpdateQrCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := h.service.repo.UpdateSiteQrCode(c.Request.Context(), siteID, id, req); err != nil {
|
||||
if writeSiteQrCodeError(c, err) {
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "更新成功"})
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteSiteQrCodeHandler(c *gin.Context) {
|
||||
siteID, ok := h.siteIDParam(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseUint(c.Param("qrcodeId"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的ID"})
|
||||
return
|
||||
}
|
||||
if err := h.service.repo.DeleteSiteQrCode(c.Request.Context(), siteID, id); err != nil {
|
||||
if writeSiteQrCodeError(c, err) {
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
|
||||
}
|
||||
|
||||
func (h *Handler) BatchDeleteSiteQrCodeHandler(c *gin.Context) {
|
||||
siteID, ok := h.siteIDParam(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
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.BatchDeleteSiteQrCodes(c.Request.Context(), siteID, req.IDs)
|
||||
if err != nil {
|
||||
if writeSiteQrCodeError(c, err) {
|
||||
return
|
||||
}
|
||||
response.RecordError(c, err)
|
||||
response.InternalServerError(c, "聊天二维码服务暂时不可用")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": gin.H{"deleted": deleted}})
|
||||
}
|
||||
Reference in New Issue
Block a user