业务更名为撞车并切换crash路由
用户可见文案与页面/API路径改为撞车与crash,保留mohong表与权限码兼容;移动端入口去掉租/撞图标。
This commit is contained in:
@@ -6,7 +6,7 @@ import (
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// MohongCategory 摸大红商品分类。
|
||||
// MohongCategory 撞车商品分类。
|
||||
type MohongCategory struct {
|
||||
ID uint64 `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"size:64;not null;uniqueIndex" json:"name"`
|
||||
@@ -31,7 +31,7 @@ func (MohongProductCategory) TableName() string {
|
||||
return "mohong_product_categories"
|
||||
}
|
||||
|
||||
// MohongProduct 摸大红商品。
|
||||
// MohongProduct 撞车商品。
|
||||
type MohongProduct struct {
|
||||
ID uint64 `gorm:"primaryKey" json:"id"`
|
||||
CategoryID *uint64 `gorm:"column:category_id;index" json:"category_id"` // 主分类(展示用)
|
||||
@@ -55,7 +55,7 @@ func (MohongProduct) TableName() string {
|
||||
return "mohong_products"
|
||||
}
|
||||
|
||||
// MohongOrder 摸大红订单。
|
||||
// MohongOrder 撞车订单。
|
||||
type MohongOrder struct {
|
||||
ID uint64 `gorm:"primaryKey" json:"id"`
|
||||
OrderNo string `gorm:"size:64;not null;uniqueIndex" json:"order_no"`
|
||||
|
||||
@@ -169,6 +169,22 @@ func (r *Repository) EnsureSupportConversation(ctx context.Context, userID uint6
|
||||
_ = tx.Model(&existing).Update("support_scene", SupportSceneGeneral).Error
|
||||
}
|
||||
}
|
||||
// 兼容旧摸大红场景码 mohong → crash
|
||||
if existing.ID == 0 && scene == SupportSceneCrash {
|
||||
err = tx.Table("chat_conversations AS c").
|
||||
Select("c.*").
|
||||
Joins("JOIN chat_participants AS cp ON cp.conversation_id = c.id").
|
||||
Where(
|
||||
"c.type = ? AND c.support_scene = ? AND cp.participant_type = ? AND cp.participant_id = ?",
|
||||
ConversationTypeGeneralSupport, SupportSceneMohong, "user", userID,
|
||||
).
|
||||
Order("c.id ASC").
|
||||
Limit(1).
|
||||
Find(&existing).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if existing.ID > 0 {
|
||||
conversationID = existing.ID
|
||||
return nil
|
||||
|
||||
@@ -35,7 +35,7 @@ type ConversationDTO struct {
|
||||
|
||||
// EnsureSupportRequest 创建/复用客服会话。
|
||||
type EnsureSupportRequest struct {
|
||||
// Scene 业务场景:general(默认)/ mohong(摸大红)
|
||||
// Scene 业务场景:general(默认)/ crash(撞车,兼容旧值 mohong)
|
||||
Scene string `json:"scene"`
|
||||
}
|
||||
|
||||
|
||||
@@ -11,18 +11,20 @@ import (
|
||||
// 客服入口业务场景(与前端 POST /chats/support 的 scene 对齐)。
|
||||
const (
|
||||
SupportSceneGeneral = "general"
|
||||
SupportSceneMohong = "mohong"
|
||||
SupportSceneCrash = "crash"
|
||||
// SupportSceneMohong 兼容旧 scene 值,与 crash 同一客服分组。
|
||||
SupportSceneMohong = "mohong"
|
||||
)
|
||||
|
||||
// resolveSupportScene 规范化场景并返回标题、客服分组 code、欢迎语。
|
||||
// groupCode 为空时表示不走分组,使用默认客服选取逻辑。
|
||||
func resolveSupportScene(raw string) (scene, title, groupCode, welcome string) {
|
||||
switch strings.ToLower(strings.TrimSpace(raw)) {
|
||||
case SupportSceneMohong:
|
||||
return SupportSceneMohong,
|
||||
"摸大红客服",
|
||||
supportgroup.GroupCodeMohong,
|
||||
"您好,这里是摸大红客服。请直接描述问题,可将订单信息复制后发送。"
|
||||
case SupportSceneCrash, SupportSceneMohong:
|
||||
return SupportSceneCrash,
|
||||
"撞车客服",
|
||||
supportgroup.GroupCodeMohong, // 分组 code 仍为 mohong,后台可改显示名
|
||||
"您好,这里是撞车客服。请直接描述问题,可将订单信息复制后发送。"
|
||||
default:
|
||||
// 未知场景回落平台客服,避免随意 scene 绕开分配策略
|
||||
return SupportSceneGeneral,
|
||||
|
||||
@@ -70,7 +70,7 @@ func (h *Handler) writeObject(c *gin.Context, publicOnly bool) {
|
||||
!strings.HasPrefix(key, "avatar/") &&
|
||||
!strings.HasPrefix(key, "payment-cert/") &&
|
||||
!strings.HasPrefix(key, "announcement/") &&
|
||||
!strings.HasPrefix(key, "mohong/") {
|
||||
!strings.HasPrefix(key, "mohong/") && !strings.HasPrefix(key, "crash/") {
|
||||
response.Error(c, http.StatusNotFound, "not_found", "文件不存在或暂不可访问")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func normalizeContentType(contentType string, data []byte) string {
|
||||
|
||||
func fileURLForScene(scene string, key string) string {
|
||||
fileURL := "/api/files/object?key=" + url.QueryEscape(key)
|
||||
if scene == "home-banner" || scene == "avatar" || scene == "payment-cert" || scene == "announcement" || scene == "mohong" {
|
||||
if scene == "home-banner" || scene == "avatar" || scene == "payment-cert" || scene == "announcement" || scene == "mohong" || scene == "crash" {
|
||||
fileURL = "/api/public/files/object?key=" + url.QueryEscape(key)
|
||||
}
|
||||
return fileURL
|
||||
@@ -115,7 +115,7 @@ func fileURLForScene(scene string, key string) string {
|
||||
func normalizeScene(scene string) string {
|
||||
scene = strings.TrimSpace(strings.ToLower(scene))
|
||||
switch scene {
|
||||
case "listing", "handoff", "dispute", "realname", "avatar", "home-banner", "chat", "payment-cert", "announcement", "qrcode", "mohong":
|
||||
case "listing", "handoff", "dispute", "realname", "avatar", "home-banner", "chat", "payment-cert", "announcement", "qrcode", "mohong", "crash":
|
||||
return scene
|
||||
default:
|
||||
return "misc"
|
||||
|
||||
@@ -14,7 +14,7 @@ const (
|
||||
configKeyDefaultQrcodeURL = "mohong.default_qrcode_url"
|
||||
configKeyOrderCopyTemplate = "mohong.order_copy_template"
|
||||
|
||||
defaultOrderCopyTemplate = "【摸大红订单】\n订单号:{{order_no}}\n下单时间:{{created_at}}\n商品明细:\n{{items}}\n数量合计:{{quantity}}\n金额:{{amount}}\n下单人:{{buyer_name}}({{buyer_phone}})"
|
||||
defaultOrderCopyTemplate = "【撞车订单】\n订单号:{{order_no}}\n下单时间:{{created_at}}\n商品明细:\n{{items}}\n数量合计:{{quantity}}\n金额:{{amount}}\n下单人:{{buyer_name}}({{buyer_phone}})"
|
||||
)
|
||||
|
||||
func (r *Repository) GetConfig(ctx context.Context) (*ConfigDTO, error) {
|
||||
@@ -42,12 +42,12 @@ func (r *Repository) UpdateConfig(ctx context.Context, req UpdateConfigRequest)
|
||||
}
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if req.DefaultQrcodeURL != nil {
|
||||
if err := upsertConfig(tx, configKeyDefaultQrcodeURL, strings.TrimSpace(*req.DefaultQrcodeURL), "摸大红全局默认固定二维码图片URL"); err != nil {
|
||||
if err := upsertConfig(tx, configKeyDefaultQrcodeURL, strings.TrimSpace(*req.DefaultQrcodeURL), "撞车全局默认固定二维码图片URL"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.OrderCopyTemplate != nil {
|
||||
if err := upsertConfig(tx, configKeyOrderCopyTemplate, strings.TrimSpace(*req.OrderCopyTemplate), "摸大红订单一键复制文案模板"); err != nil {
|
||||
if err := upsertConfig(tx, configKeyOrderCopyTemplate, strings.TrimSpace(*req.OrderCopyTemplate), "撞车订单一键复制文案模板"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ func (r *Repository) CreateOrder(ctx context.Context, userID uint64, req CreateO
|
||||
if err := notification.Append(tx, notification.Entry{
|
||||
UserID: userID,
|
||||
Type: "order",
|
||||
Title: "摸大红订单已创建",
|
||||
Title: "撞车订单已创建",
|
||||
Content: "订单已创建,请在有效时间内完成支付。",
|
||||
BizType: "mohong_order",
|
||||
BizID: &orderID,
|
||||
@@ -311,8 +311,8 @@ func (r *Repository) AdminCompleteOrder(ctx context.Context, orderID uint64, rem
|
||||
return r.AdminFindOrder(ctx, orderID)
|
||||
}
|
||||
|
||||
// ConfirmPaidFromChannelTx 支付成功后推进摸大红订单:固化二维码快照与可复制文案。
|
||||
// 返回值固定为 0,兼容支付模块会话通知签名(摸大红无订单群)。
|
||||
// ConfirmPaidFromChannelTx 支付成功后推进撞车订单:固化二维码快照与可复制文案。
|
||||
// 返回值固定为 0,兼容支付模块会话通知签名(撞车无订单群)。
|
||||
func (r *Repository) ConfirmPaidFromChannelTx(tx *gorm.DB, orderID uint64) (uint64, error) {
|
||||
var order model.MohongOrder
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
|
||||
@@ -348,7 +348,7 @@ func (r *Repository) ConfirmPaidFromChannelTx(tx *gorm.DB, orderID uint64) (uint
|
||||
if err := notification.Append(tx, notification.Entry{
|
||||
UserID: order.UserID,
|
||||
Type: "order",
|
||||
Title: "摸大红订单支付成功",
|
||||
Title: "撞车订单支付成功",
|
||||
Content: "支付已完成,请在订单详情扫码联系客服,并复制订单信息发送。",
|
||||
BizType: "mohong_order",
|
||||
BizID: &orderIDCopy,
|
||||
@@ -358,7 +358,7 @@ func (r *Repository) ConfirmPaidFromChannelTx(tx *gorm.DB, orderID uint64) (uint
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// NotifyNewConversation 兼容支付模块接口;摸大红不创建会话,空实现。
|
||||
// NotifyNewConversation 兼容支付模块接口;撞车不创建会话,空实现。
|
||||
func (r *Repository) NotifyNewConversation(conversationID uint64) {}
|
||||
|
||||
func (r *Repository) cancelPendingOrderTx(tx *gorm.DB, order *model.MohongOrder, reason string) error {
|
||||
|
||||
@@ -2,7 +2,7 @@ package mohong
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
// Repository 摸大红数据访问。
|
||||
// Repository 撞车数据访问。
|
||||
type Repository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
const bizTypeMohongPay = "mohong_pay"
|
||||
|
||||
// StartMohong 发起摸大红订单支付。
|
||||
// StartMohong 发起撞车订单支付。
|
||||
func (r *Repository) StartMohong(ctx context.Context, userID uint64, orderID uint64, req StartPaymentRequest, clientIP string) (*PaymentDTO, error) {
|
||||
req.PayWay = normalizePayWay(req.PayWay)
|
||||
defaultConfig, err := r.defaultRuntimeConfig(ctx, req.PayWay)
|
||||
@@ -73,7 +73,7 @@ func (r *Repository) StartMohong(ctx context.Context, userID uint64, orderID uin
|
||||
NotifyURL: runtimeConfig.NotifyURL,
|
||||
JumpURL: runtimeConfig.JumpURL,
|
||||
ClientIP: clientIP,
|
||||
Body: "摸大红订单 " + orderRow.OrderNo,
|
||||
Body: "撞车订单 " + orderRow.OrderNo,
|
||||
Attach: orderRow.OrderNo,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -202,7 +202,7 @@ func newMohongPayment(row model.MohongOrder, req StartPaymentRequest, runtimeCon
|
||||
return payment, nil
|
||||
}
|
||||
|
||||
// QueryMohong 查询摸大红订单支付状态。
|
||||
// QueryMohong 查询撞车订单支付状态。
|
||||
func (r *Repository) QueryMohong(ctx context.Context, userID uint64, orderID uint64) (*PaymentDTO, error) {
|
||||
var payment model.PaymentOrder
|
||||
if err := r.db.WithContext(ctx).
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MohongOrderConfirmer 摸大红订单支付确认接口,避免 payment 与 mohong 循环依赖。
|
||||
// MohongOrderConfirmer 撞车订单支付确认接口,避免 payment 与 mohong 循环依赖。
|
||||
type MohongOrderConfirmer interface {
|
||||
ConfirmPaidFromChannelTx(tx *gorm.DB, orderID uint64) (uint64, error)
|
||||
NotifyNewConversation(conversationID uint64)
|
||||
@@ -89,7 +89,7 @@ func WithLogger(logger *zap.Logger) RepositoryOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithMohongRepo 注入摸大红订单确认器。
|
||||
// WithMohongRepo 注入撞车订单确认器。
|
||||
func WithMohongRepo(repo MohongOrderConfirmer) RepositoryOption {
|
||||
return func(r *Repository) {
|
||||
r.mohongRepo = repo
|
||||
|
||||
@@ -428,23 +428,26 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
|
||||
sellerPickupRoutes.GET("", pickupHandler.ListForSeller)
|
||||
}
|
||||
|
||||
// 摸大红:商品公开,下单需登录+实名
|
||||
// 撞车(对外路径 /crash;兼容旧路径 /mohong):商品公开,下单需登录+实名
|
||||
if mohongHandler != nil {
|
||||
mohongPublic := api.Group("/mohong")
|
||||
{
|
||||
mohongPublic.GET("/categories", mohongHandler.ListCategories)
|
||||
mohongPublic.GET("/products", mohongHandler.ListProducts)
|
||||
mohongPublic.GET("/products/:id", mohongHandler.GetProduct)
|
||||
registerCrashPublic := func(g *gin.RouterGroup) {
|
||||
g.GET("/categories", mohongHandler.ListCategories)
|
||||
g.GET("/products", mohongHandler.ListProducts)
|
||||
g.GET("/products/:id", mohongHandler.GetProduct)
|
||||
}
|
||||
mohongAuth := api.Group("/mohong", requireAuth)
|
||||
{
|
||||
mohongAuth.POST("/orders", requireRealname, mohongHandler.CreateOrder)
|
||||
mohongAuth.GET("/orders", mohongHandler.ListMyOrders)
|
||||
mohongAuth.GET("/orders/:id", mohongHandler.GetMyOrder)
|
||||
mohongAuth.POST("/orders/:id/cancel", mohongHandler.CancelMyOrder)
|
||||
mohongAuth.POST("/orders/:id/start-payment", requireRealname, paymentHandler.StartMohong)
|
||||
mohongAuth.GET("/orders/:id/query-payment", paymentHandler.QueryMohong)
|
||||
registerCrashAuth := func(g *gin.RouterGroup) {
|
||||
g.POST("/orders", requireRealname, mohongHandler.CreateOrder)
|
||||
g.GET("/orders", mohongHandler.ListMyOrders)
|
||||
g.GET("/orders/:id", mohongHandler.GetMyOrder)
|
||||
g.POST("/orders/:id/cancel", mohongHandler.CancelMyOrder)
|
||||
g.POST("/orders/:id/start-payment", requireRealname, paymentHandler.StartMohong)
|
||||
g.GET("/orders/:id/query-payment", paymentHandler.QueryMohong)
|
||||
}
|
||||
registerCrashPublic(api.Group("/crash"))
|
||||
registerCrashAuth(api.Group("/crash", requireAuth))
|
||||
// 兼容旧 API 前缀
|
||||
registerCrashPublic(api.Group("/mohong"))
|
||||
registerCrashAuth(api.Group("/mohong", requireAuth))
|
||||
}
|
||||
|
||||
orderRoutes := api.Group("/orders", requireAuth)
|
||||
@@ -599,24 +602,28 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
|
||||
adminRoutes.POST("/listings/:id/offline", requirePerm("listing:offline"), listingHandler.AdminOffline)
|
||||
adminRoutes.POST("/listings/:id/mark-abnormal", requirePerm("listing:offline"), listingHandler.AdminMarkAbnormal)
|
||||
|
||||
// 摸大红
|
||||
// 撞车后台(/admin/crash;兼容 /admin/mohong)
|
||||
if mohongHandler != nil {
|
||||
// 商品编辑需要拉分类列表,故 GET 用 product_view
|
||||
adminRoutes.GET("/mohong/categories", requirePerm("mohong:product_view"), mohongHandler.AdminListCategories)
|
||||
adminRoutes.POST("/mohong/categories", requirePerm("mohong:category"), mohongHandler.AdminCreateCategory)
|
||||
adminRoutes.PUT("/mohong/categories/:id", requirePerm("mohong:category"), mohongHandler.AdminUpdateCategory)
|
||||
adminRoutes.DELETE("/mohong/categories/:id", requirePerm("mohong:category"), mohongHandler.AdminDeleteCategory)
|
||||
adminRoutes.GET("/mohong/products", requirePerm("mohong:product_view"), mohongHandler.AdminListProducts)
|
||||
adminRoutes.GET("/mohong/products/:id", requirePerm("mohong:product_view"), mohongHandler.AdminGetProduct)
|
||||
adminRoutes.POST("/mohong/products", requirePerm("mohong:product_manage"), mohongHandler.AdminCreateProduct)
|
||||
adminRoutes.PUT("/mohong/products/:id", requirePerm("mohong:product_manage"), mohongHandler.AdminUpdateProduct)
|
||||
adminRoutes.DELETE("/mohong/products/:id", requirePerm("mohong:product_manage"), mohongHandler.AdminDeleteProduct)
|
||||
adminRoutes.GET("/mohong/orders", requirePerm("mohong:order_view"), mohongHandler.AdminListOrders)
|
||||
adminRoutes.GET("/mohong/orders/:id", requirePerm("mohong:order_view"), mohongHandler.AdminGetOrder)
|
||||
adminRoutes.POST("/mohong/orders/:id/complete", requirePerm("mohong:order_manage"), mohongHandler.AdminCompleteOrder)
|
||||
adminRoutes.POST("/mohong/orders/:id/cancel", requirePerm("mohong:order_manage"), mohongHandler.AdminCancelOrder)
|
||||
adminRoutes.GET("/mohong/config", requirePerm("mohong:config"), mohongHandler.AdminGetConfig)
|
||||
adminRoutes.PUT("/mohong/config", requirePerm("mohong:config"), mohongHandler.AdminUpdateConfig)
|
||||
registerCrashAdmin := func(prefix string) {
|
||||
adminRoutes.GET(prefix+"/categories", requirePerm("mohong:product_view"), mohongHandler.AdminListCategories)
|
||||
adminRoutes.POST(prefix+"/categories", requirePerm("mohong:category"), mohongHandler.AdminCreateCategory)
|
||||
adminRoutes.PUT(prefix+"/categories/:id", requirePerm("mohong:category"), mohongHandler.AdminUpdateCategory)
|
||||
adminRoutes.DELETE(prefix+"/categories/:id", requirePerm("mohong:category"), mohongHandler.AdminDeleteCategory)
|
||||
adminRoutes.GET(prefix+"/products", requirePerm("mohong:product_view"), mohongHandler.AdminListProducts)
|
||||
adminRoutes.GET(prefix+"/products/:id", requirePerm("mohong:product_view"), mohongHandler.AdminGetProduct)
|
||||
adminRoutes.POST(prefix+"/products", requirePerm("mohong:product_manage"), mohongHandler.AdminCreateProduct)
|
||||
adminRoutes.PUT(prefix+"/products/:id", requirePerm("mohong:product_manage"), mohongHandler.AdminUpdateProduct)
|
||||
adminRoutes.DELETE(prefix+"/products/:id", requirePerm("mohong:product_manage"), mohongHandler.AdminDeleteProduct)
|
||||
adminRoutes.GET(prefix+"/orders", requirePerm("mohong:order_view"), mohongHandler.AdminListOrders)
|
||||
adminRoutes.GET(prefix+"/orders/:id", requirePerm("mohong:order_view"), mohongHandler.AdminGetOrder)
|
||||
adminRoutes.POST(prefix+"/orders/:id/complete", requirePerm("mohong:order_manage"), mohongHandler.AdminCompleteOrder)
|
||||
adminRoutes.POST(prefix+"/orders/:id/cancel", requirePerm("mohong:order_manage"), mohongHandler.AdminCancelOrder)
|
||||
adminRoutes.GET(prefix+"/config", requirePerm("mohong:config"), mohongHandler.AdminGetConfig)
|
||||
adminRoutes.PUT(prefix+"/config", requirePerm("mohong:config"), mohongHandler.AdminUpdateConfig)
|
||||
}
|
||||
registerCrashAdmin("/crash")
|
||||
registerCrashAdmin("/mohong")
|
||||
}
|
||||
|
||||
adminRoutes.GET("/disputes", requirePerm("dispute:view"), disputeHandler.AdminList)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
-- +goose Up
|
||||
|
||||
UPDATE chat_support_groups
|
||||
SET name = '撞车-客服',
|
||||
description = '用户从撞车入口联系客服时,按本组成员在线与负载分配'
|
||||
WHERE code = 'mohong';
|
||||
|
||||
UPDATE system_configs
|
||||
SET description = REPLACE(description, '摸大红', '撞车'),
|
||||
`value` = REPLACE(`value`, '摸大红', '撞车')
|
||||
WHERE `key` LIKE 'mohong.%';
|
||||
|
||||
UPDATE permissions
|
||||
SET name = REPLACE(name, '摸大红', '撞车')
|
||||
WHERE code LIKE 'mohong:%';
|
||||
|
||||
-- +goose Down
|
||||
|
||||
UPDATE chat_support_groups
|
||||
SET name = '摸大红-客服',
|
||||
description = '用户从摸大红入口联系客服时,按本组成员在线与负载分配'
|
||||
WHERE code = 'mohong';
|
||||
|
||||
UPDATE system_configs
|
||||
SET description = REPLACE(description, '撞车', '摸大红'),
|
||||
`value` = REPLACE(`value`, '撞车', '摸大红')
|
||||
WHERE `key` LIKE 'mohong.%';
|
||||
|
||||
UPDATE permissions
|
||||
SET name = REPLACE(name, '撞车', '摸大红')
|
||||
WHERE code LIKE 'mohong:%';
|
||||
Reference in New Issue
Block a user