From 0a85634a58a07c45fa07dd37c8187a77dc773ef8 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 30 Oct 2026 17:50:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor(chat):=20=E6=AC=A2=E8=BF=8E=E8=AF=AD?= =?UTF-8?q?=E4=BB=85=E4=BF=9D=E7=95=99=E8=B4=A6=E5=8F=B7=E7=BE=A4=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/chat/handler_quick_reply.go | 20 ----- backend/internal/modules/chat/participant.go | 6 -- backend/internal/modules/chat/quick_reply.go | 15 ---- backend/internal/modules/chat/repository.go | 85 ------------------- backend/internal/modules/chat/service.go | 14 --- .../modules/systemconfig/repository.go | 2 - backend/internal/router/router.go | 2 - backend/migrations/000001_init.sql | 3 +- docs/Phase2-用户侧与摸红商城验收.md | 2 +- docs/项目计划.md | 2 +- .../features/admin/AdminChatWelcomePage.tsx | 76 ++++------------- frontend/src/features/admin/api.ts | 15 ---- frontend/src/features/messages/ChatPage.tsx | 2 +- .../src/features/messages/MessagesPage.tsx | 7 +- .../features/mobile/MobileMessagesPage.tsx | 4 +- .../features/mobile/MobileOrderDetailPage.tsx | 4 +- .../src/features/orders/OrderDetailPage.tsx | 4 +- frontend/src/layouts/AdminLayout.tsx | 2 +- 18 files changed, 29 insertions(+), 236 deletions(-) diff --git a/backend/internal/modules/chat/handler_quick_reply.go b/backend/internal/modules/chat/handler_quick_reply.go index ca24791..48e0b93 100644 --- a/backend/internal/modules/chat/handler_quick_reply.go +++ b/backend/internal/modules/chat/handler_quick_reply.go @@ -78,26 +78,6 @@ func (h *Handler) AdminDeleteQuickReply(c *gin.Context) { response.OK(c, gin.H{"deleted": true}) } -func (h *Handler) AdminGetAutoWelcome(c *gin.Context) { - message := h.service.GetAutoWelcomeMessage(c.Request.Context()) - response.OK(c, gin.H{"message": message}) -} - -func (h *Handler) AdminUpdateAutoWelcome(c *gin.Context) { - var req struct { - Message string `json:"message" binding:"required"` - } - if err := c.ShouldBindJSON(&req); err != nil { - response.BadRequest(c, "话术内容不能为空") - return - } - if err := h.service.UpdateAutoWelcomeMessage(c.Request.Context(), req.Message); err != nil { - writeChatError(c, err) - return - } - response.OK(c, gin.H{"updated": true}) -} - func (h *Handler) AdminGetListingGroupWelcome(c *gin.Context) { message := h.service.GetListingGroupWelcomeMessage(c.Request.Context()) response.OK(c, gin.H{"message": message}) diff --git a/backend/internal/modules/chat/participant.go b/backend/internal/modules/chat/participant.go index 9bbbc76..463498b 100644 --- a/backend/internal/modules/chat/participant.go +++ b/backend/internal/modules/chat/participant.go @@ -433,12 +433,6 @@ func adminIsSupport(tx *gorm.DB, id uint64) bool { } return count > 0 } -func orderConversationTitle(order model.RentalOrder) string { - if order.OrderNo == "" { - return fmt.Sprintf("订单群聊 #%d", order.ID) - } - return "订单群聊 " + order.OrderNo -} func fallbackName(participantType string, id uint64, name string) string { if name != "" { return name diff --git a/backend/internal/modules/chat/quick_reply.go b/backend/internal/modules/chat/quick_reply.go index d7bebd7..1f4d7ee 100644 --- a/backend/internal/modules/chat/quick_reply.go +++ b/backend/internal/modules/chat/quick_reply.go @@ -11,8 +11,6 @@ import ( ) const ( - autoWelcomeConfigKey = "chat.auto_welcome_message" - defaultAutoWelcomeMessage = "欢迎加入订单群聊!如有任何问题,请随时沟通。" listingGroupWelcomeConfigKey = "chat.listing_group_welcome" defaultListingGroupWelcome = "欢迎加入账号群!请号主扫描下方二维码加入企业微信群,方便客服与您及时联系。" ) @@ -104,19 +102,6 @@ func (r *Repository) DeleteQuickReply(ctx context.Context, adminID uint64, reply return r.db.WithContext(ctx).Where("id = ? AND admin_user_id = ?", replyID, adminID). Delete(&model.ChatQuickReply{}).Error } -func (r *Repository) GetAutoWelcomeMessage(ctx context.Context) string { - var cfg model.SystemConfig - if err := r.db.WithContext(ctx).Where("`key` = ?", autoWelcomeConfigKey).First(&cfg).Error; err != nil { - return defaultAutoWelcomeMessage - } - return cfg.Value -} -func (r *Repository) UpdateAutoWelcomeMessage(ctx context.Context, message string) error { - return r.db.WithContext(ctx).Model(&model.SystemConfig{}). - Where("`key` = ?", autoWelcomeConfigKey). - Update("value", message).Error -} - func (r *Repository) GetListingGroupWelcomeMessage(ctx context.Context) string { var cfg model.SystemConfig if err := r.db.WithContext(ctx).Where("`key` = ?", listingGroupWelcomeConfigKey).First(&cfg).Error; err != nil { diff --git a/backend/internal/modules/chat/repository.go b/backend/internal/modules/chat/repository.go index 62dd8f0..c1312d5 100644 --- a/backend/internal/modules/chat/repository.go +++ b/backend/internal/modules/chat/repository.go @@ -2,15 +2,12 @@ package chat import ( "context" - "errors" "net/http" "time" "github.com/redis/go-redis/v9" "go.uber.org/zap" "gorm.io/gorm" - "gorm.io/gorm/clause" - "hfb_sys/backend/internal/model" "hfb_sys/backend/internal/modules/chathub" ) @@ -77,88 +74,6 @@ func NewRepository(db *gorm.DB, hub *chathub.Hub, redis *redis.Client, options . } return repo } -func EnsureOrderConversation(tx *gorm.DB, order model.RentalOrder) (*model.ChatConversation, error) { - var existing model.ChatConversation - err := tx.Where("order_id = ?", order.ID).First(&existing).Error - if err == nil { - return &existing, nil - } - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } - - var siteID *uint64 - if order.DistributionSiteID != nil && *order.DistributionSiteID > 0 && order.DistributionMode != "two_party_distributor_receive" { - siteID = order.DistributionSiteID - } - now := time.Now() - conversation := model.ChatConversation{ - OrderID: &order.ID, - DistributionSiteID: siteID, - Type: "order_group", - Title: orderConversationTitle(order), - Status: "active", - } - if err := tx.Create(&conversation).Error; err != nil { - return nil, err - } - - participants := []model.ChatParticipant{ - { - ConversationID: conversation.ID, - ParticipantType: "user", - ParticipantID: order.RenterID, - Role: "renter", - JoinedAt: now, - }, - { - ConversationID: conversation.ID, - ParticipantType: "user", - ParticipantID: order.OwnerID, - Role: "owner", - JoinedAt: now, - }, - } - if supportID := defaultSupportAdminID(tx); supportID > 0 { - participants = append(participants, model.ChatParticipant{ - ConversationID: conversation.ID, - ParticipantType: "admin", - ParticipantID: supportID, - Role: "support", - JoinedAt: now, - }) - } - for _, participant := range participants { - if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&participant).Error; err != nil { - return nil, err - } - } - - // 获取自动话术 - autoMessage := "订单已支付,群聊已创建。租客、号主和客服可在这里沟通交接与结账问题。" - var cfg model.SystemConfig - if err := tx.Where("`key` = ?", "chat.auto_welcome_message").First(&cfg).Error; err == nil && cfg.Value != "" { - autoMessage = cfg.Value - } - message := model.ChatMessage{ - ConversationID: conversation.ID, - SenderType: "system", - SenderRole: "system", - ContentType: "system", - Content: autoMessage, - AttachmentURLS: emptyJSONList(), - } - if err := tx.Create(&message).Error; err != nil { - return nil, err - } - conversation.LastMessageID = &message.ID - conversation.LastMessagePreview = truncatePreview(message.Content) - conversation.LastMessageAt = &message.CreatedAt - if err := tx.Save(&conversation).Error; err != nil { - return nil, err - } - return &conversation, nil -} func (r *Repository) NotifyNewConversation(conversationID uint64) { if r.hub == nil { return diff --git a/backend/internal/modules/chat/service.go b/backend/internal/modules/chat/service.go index 3e8be1f..dc06d5c 100644 --- a/backend/internal/modules/chat/service.go +++ b/backend/internal/modules/chat/service.go @@ -216,20 +216,6 @@ func (s *Service) DeleteQuickReply(ctx context.Context, adminID uint64, replyID return s.repo.DeleteQuickReply(ctx, adminID, replyID) } -func (s *Service) GetAutoWelcomeMessage(ctx context.Context) string { - if s.repo == nil { - return "" - } - return s.repo.GetAutoWelcomeMessage(ctx) -} - -func (s *Service) UpdateAutoWelcomeMessage(ctx context.Context, message string) error { - if s.repo == nil { - return ErrDependencyUnavailable - } - return s.repo.UpdateAutoWelcomeMessage(ctx, message) -} - func (s *Service) GetListingGroupWelcomeMessage(ctx context.Context) string { if s.repo == nil { return "" diff --git a/backend/internal/modules/systemconfig/repository.go b/backend/internal/modules/systemconfig/repository.go index 90fe249..9e36d9a 100644 --- a/backend/internal/modules/systemconfig/repository.go +++ b/backend/internal/modules/systemconfig/repository.go @@ -45,7 +45,6 @@ var defaultConfigs = []defaultConfig{ {Key: rentergrowth.ConfigKey, Value: rentergrowth.DefaultConfigValue(), Description: "租客成长等级规则(积分门槛、租金折扣、等级免押额度)"}, {Key: smsnode.ConfigKey, Value: smsnode.DefaultConfigValue(), Description: "节点短信模板配置(总开关、各业务节点模板 CODE)"}, {Key: "chat.default_support_admin_id", Value: "2", Description: "默认客服 ID(必须是启用状态的客服角色)"}, - {Key: "chat.auto_welcome_message", Value: "欢迎加入订单群聊!如有任何问题,请随时沟通。", Description: "建群后自动发送的欢迎话术"}, {Key: "chat.listing_group_welcome", Value: "欢迎加入账号群!请号主扫描下方二维码加入企业微信群,方便客服与您及时联系。", Description: "发布群创建后自动发送的欢迎语"}, {Key: "chat.renter_retention_days_after_order_end", Value: "5", Description: "订单结束后租客保留在发布群的天数(3-7)"}, {Key: "integration.paddle_ocr_token", Value: "", Description: "PaddleOCR API Token,用于二维码群名自动识别"}, @@ -66,7 +65,6 @@ var defaultConfigs = []defaultConfig{ } var adminVisibleConfigKeys = []string{ - "chat.auto_welcome_message", "chat.default_support_admin_id", "chat.listing_group_welcome", "chat.renter_retention_days_after_order_end", diff --git a/backend/internal/router/router.go b/backend/internal/router/router.go index e665260..473aca7 100644 --- a/backend/internal/router/router.go +++ b/backend/internal/router/router.go @@ -896,8 +896,6 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine { adminRoutes.POST("/chats/quick-replies", requirePerm("chat:send"), chatHandler.AdminCreateQuickReply) adminRoutes.PUT("/chats/quick-replies/:id", requirePerm("chat:send"), chatHandler.AdminUpdateQuickReply) adminRoutes.DELETE("/chats/quick-replies/:id", requirePerm("chat:send"), chatHandler.AdminDeleteQuickReply) - adminRoutes.GET("/chats/auto-welcome", requirePerm("system_config:view"), chatHandler.AdminGetAutoWelcome) - adminRoutes.PUT("/chats/auto-welcome", requirePerm("system_config:update"), chatHandler.AdminUpdateAutoWelcome) adminRoutes.GET("/chats/listing-group-welcome", requirePerm("system_config:view"), chatHandler.AdminGetListingGroupWelcome) adminRoutes.PUT("/chats/listing-group-welcome", requirePerm("system_config:update"), chatHandler.AdminUpdateListingGroupWelcome) diff --git a/backend/migrations/000001_init.sql b/backend/migrations/000001_init.sql index b1cd716..e5696f1 100644 --- a/backend/migrations/000001_init.sql +++ b/backend/migrations/000001_init.sql @@ -666,8 +666,7 @@ WHERE r.code = 'finance' AND p.code IN ( INSERT INTO system_configs (`key`, `value`, description) VALUES ('listing.review_required', 'true', '商品是否需要审核'), ('order.pending_payment_timeout_minutes', '15', '待支付超时时间(分钟)'), -('chat.default_support_admin_id', '2', '默认客服 ID(必须是启用状态的客服角色)'), -('chat.auto_welcome_message', '欢迎加入订单群聊!如有任何问题,请随时沟通。', '建群后自动发送的欢迎话术') +('chat.default_support_admin_id', '2', '默认客服 ID(必须是启用状态的客服角色)') ON DUPLICATE KEY UPDATE `value` = VALUES(`value`), description = VALUES(description); diff --git a/docs/Phase2-用户侧与摸红商城验收.md b/docs/Phase2-用户侧与摸红商城验收.md index a05ae3e..6cb2653 100644 --- a/docs/Phase2-用户侧与摸红商城验收.md +++ b/docs/Phase2-用户侧与摸红商城验收.md @@ -82,5 +82,5 @@ - 所有写操作(注册、下单、支付、退款、分类/商品增删改、备份计划保存)未执行真实写入;支付与短信/实名仍为空配置。 - 摸红分类状态值后端为 `enabled/disabled`,后台分类页下拉目前使用 `active/disabled`,需在真实数据联调时统一(已记录待修)。 - 商品合集 `/listings/collections` 新旧后端均无收藏表/接口,旧站该路由实为「商品合集」,本轮未复刻。 -- 二维码池已支持批量导入(单次 ≤20 张)与 OCR 识别群名;欢迎语配置(订单群 / 账号群)已落地 `/console-jy8k3m/chat-welcome`。OCR 依赖系统配置中的 PaddleOCR Token,未配置时页面会提示失败,需真实 Token 验证识别效果。 +- 二维码池已支持批量导入(单次 ≤20 张)与 OCR 识别群名;欢迎语配置仅保留账号群(`/console-jy8k3m/chat-welcome` → `/admin/chats/listing-group-welcome`),订单群欢迎语相关前后端代码已移除。OCR 依赖系统配置中的 PaddleOCR Token,未配置时页面会提示失败,需真实 Token 验证识别效果。 - 主入口已接近 500 kB,后续新增页面建议按路由懒加载并在需要时再评估拆包。 diff --git a/docs/项目计划.md b/docs/项目计划.md index 6ab3aa5..b1b797c 100644 --- a/docs/项目计划.md +++ b/docs/项目计划.md @@ -414,7 +414,7 @@ hfb_jysh/ - 已落地:`/wallet`(余额 + 流水)、`/wallet/payment-accounts`、`/wallet/withdrawal`、移动端 `/m/wallet/withdrawal`、卖家 `/seller/earnings`、`/seller/handoffs`、`/seller/pickups`。 - 申诉仲裁、手工订单、提号、客服群、快捷回复。 - 已落地:客服群组(`/chat-support-groups`,权限 `chat:manage`),角色权限矩阵(`/roles`,权限 `role:manage`),手工录单(`/manual-entries`,权限 `cs_manual_entry:*`),快捷回复(`/chat-quick-replies`,权限 `chat:view` / `chat:send`),线下提号(`/pickup`、`/pickup/:id`,权限 `order:pickup`,口径统一为「线下提号」)。 - - 已落地:欢迎语配置 `/chat-welcome`(订单群 + 账号群),二维码池批量导入与 OCR 识别群名。 + - 已落地:欢迎语配置 `/chat-welcome`(仅账号群),二维码池批量导入与 OCR 识别群名。 - 分销站点、规则、订单、结算、二维码池、分销用户、公告。 - 公告、邀请返利、推送、追踪链接。 - 已落地:公告管理、邀请管理(返利配置/邀请关系/返利流水)、推广追踪(`/tracking-links`)、通知中心(`/notifications`)、推送通知(`/push`)。 diff --git a/frontend/src/features/admin/AdminChatWelcomePage.tsx b/frontend/src/features/admin/AdminChatWelcomePage.tsx index 29be1ab..31a7ebd 100644 --- a/frontend/src/features/admin/AdminChatWelcomePage.tsx +++ b/frontend/src/features/admin/AdminChatWelcomePage.tsx @@ -3,12 +3,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query' import { Alert, App, Button, Card, Form, Input, Space, Tag, Typography } from 'antd' import { ReloadOutlined } from '@ant-design/icons' -import { - fetchChatAutoWelcome, - fetchListingGroupWelcome, - updateChatAutoWelcome, - updateListingGroupWelcome, -} from '@/features/admin/api' +import { fetchListingGroupWelcome, updateListingGroupWelcome } from '@/features/admin/api' import { AdminPageHeader } from '@/features/admin/components/AdminPageHeader' import { useAdminSessionStore } from '@/stores/adminSession' @@ -20,30 +15,20 @@ function errorText(error: unknown) { export function AdminChatWelcomePage() { const { message } = App.useApp() const queryClient = useQueryClient() - const [autoForm] = Form.useForm<{ message: string }>() const [groupForm] = Form.useForm<{ message: string }>() - const [saving, setSaving] = useState<'auto' | 'group' | null>(null) + const [saving, setSaving] = useState(false) const permissions = useAdminSessionStore(state => state.admin?.permissions) const canView = Boolean(permissions?.includes('*') || permissions?.includes('system_config:view')) const canManage = Boolean( permissions?.includes('*') || permissions?.includes('system_config:update') ) - const autoQuery = useQuery({ - queryKey: ['chat-auto-welcome'], - queryFn: fetchChatAutoWelcome, - enabled: canView, - }) const groupQuery = useQuery({ queryKey: ['chat-listing-group-welcome'], queryFn: fetchListingGroupWelcome, enabled: canView, }) - useEffect(() => { - if (autoQuery.data != null) autoForm.setFieldsValue({ message: autoQuery.data }) - }, [autoQuery.data, autoForm]) - useEffect(() => { if (groupQuery.data != null) groupForm.setFieldsValue({ message: groupQuery.data }) }, [groupQuery.data, groupForm]) @@ -57,23 +42,17 @@ export function AdminChatWelcomePage() { ) } - const save = async (kind: 'auto' | 'group') => { - const form = kind === 'auto' ? autoForm : groupForm - const values = await form.validateFields() - setSaving(kind) + const save = async () => { + const values = await groupForm.validateFields() + setSaving(true) try { - if (kind === 'auto') { - await updateChatAutoWelcome(values.message.trim()) - await queryClient.invalidateQueries({ queryKey: ['chat-auto-welcome'] }) - } else { - await updateListingGroupWelcome(values.message.trim()) - await queryClient.invalidateQueries({ queryKey: ['chat-listing-group-welcome'] }) - } + await updateListingGroupWelcome(values.message.trim()) + await queryClient.invalidateQueries({ queryKey: ['chat-listing-group-welcome'] }) message.success('欢迎语已保存') } catch (error) { message.error(errorText(error)) } finally { - setSaving(null) + setSaving(false) } } @@ -82,49 +61,24 @@ export function AdminChatWelcomePage() { - 订单群 账号群 } extra={ } /> - -
- - - - -
-
- - +
@@ -146,7 +100,7 @@ export function AdminChatWelcomePage() { - 欢迎语保存在系统配置中,修改后对新建立的群生效。 + 欢迎语保存在系统配置中,修改后对新建立的账号群生效。 ) diff --git a/frontend/src/features/admin/api.ts b/frontend/src/features/admin/api.ts index 1e7d667..16f0036 100644 --- a/frontend/src/features/admin/api.ts +++ b/frontend/src/features/admin/api.ts @@ -2027,21 +2027,6 @@ export async function updateCrashConfig(payload: Partial) { return data.data } -export async function fetchChatAutoWelcome() { - const { data } = await adminClient.get>( - '/admin/chats/auto-welcome' - ) - return data.data.message ?? '' -} - -export async function updateChatAutoWelcome(message: string) { - const { data } = await adminClient.put>( - '/admin/chats/auto-welcome', - { message } - ) - return data.data -} - export async function fetchListingGroupWelcome() { const { data } = await adminClient.get>( '/admin/chats/listing-group-welcome' diff --git a/frontend/src/features/messages/ChatPage.tsx b/frontend/src/features/messages/ChatPage.tsx index 3a81b58..960eab6 100644 --- a/frontend/src/features/messages/ChatPage.tsx +++ b/frontend/src/features/messages/ChatPage.tsx @@ -210,7 +210,7 @@ export function ChatPage() { 登录后进入会话 - 订单群聊与客服会话只对当前账号开放。 + 账号群聊与客服会话只对当前账号开放。 @@ -185,7 +185,7 @@ export function MobileMessagesPage() { ) : chatItems.length === 0 ? (
- 下单后会自动创建订单群聊,也可以先联系客服。 + 商品发布后会自动创建账号群,也可以先联系客服。
) : (
diff --git a/frontend/src/features/mobile/MobileOrderDetailPage.tsx b/frontend/src/features/mobile/MobileOrderDetailPage.tsx index a01e4eb..418380e 100644 --- a/frontend/src/features/mobile/MobileOrderDetailPage.tsx +++ b/frontend/src/features/mobile/MobileOrderDetailPage.tsx @@ -54,7 +54,7 @@ export function MobileOrderDetailPage() { } catch (error) { Toast.show({ icon: 'fail', - content: error instanceof Error ? error.message : '订单群聊暂时无法打开', + content: error instanceof Error ? error.message : '账号群暂时无法打开', }) } finally { setOpeningChat(false) @@ -254,7 +254,7 @@ export function MobileOrderDetailPage() { loading={openingChat} onClick={() => void openOrderChat()} > - 进入订单群聊 + 进入账号群 void openOrderChat()} > - 进入订单群聊 + 进入账号群 = { }, '/chat-welcome': { title: '欢迎语配置', - description: '维护订单群与账号群的自动欢迎语。', + description: '维护账号群的自动欢迎语。', }, '/qrcode-pool': { title: '二维码池',