2026-07-30 14:16:58 +08:00
package gas
import (
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/heqiapp/platforms/backend/api/internal/impl"
"git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common"
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func ticketQuery ( gasID uint64 ) * gorm . DB {
2026-08-22 10:48:36 +08:00
return ticketQueryWithDB ( impl . DBService , gasID )
}
// ticketQueryWithDB 统一客服工单的数据范围查询,并允许只读 SQL 测试注入数据库连接。
func ticketQueryWithDB ( databaseService * gorm . DB , gasID uint64 ) * gorm . DB {
return common . ActiveRecords ( databaseService . Model ( & models . CsTicket { } ) ) .
2026-07-30 14:16:58 +08:00
Joins ( "JOIN user_service_relation ON user_service_relation.user_account_id = cs_ticket.user_account_id AND user_service_relation.status <> ?" ,
common . StatusArchived ) .
2026-09-13 00:57:32 +08:00
Where ( "((cs_ticket.gasorder_contract_id = 0 AND user_service_relation.gas_basic_id = ?) OR (cs_ticket.gasorder_contract_id <> 0 AND cs_ticket.gas_basic_id = ?))" , gasID , gasID )
2026-07-30 14:16:58 +08:00
}
2026-08-22 10:48:36 +08:00
// ticketDetailDisplay 为工单详情附加当前记录所引用关系的可读名称,不修改工单归属数据。
type ticketDetailDisplay struct {
models . CsTicket
2026-09-13 00:57:32 +08:00
ContractRequestOpen int ` gorm:"column:contract_request_open" json:"contract_request_open" `
2026-08-22 10:48:36 +08:00
UserAccountDisplayName string ` gorm:"column:user_account_display_name" json:"user_account_display_name" `
DeliveryBasicDisplayName string ` gorm:"column:delivery_basic_display_name" json:"delivery_basic_display_name" `
StaffAccountDisplayName string ` gorm:"column:staff_account_display_name" json:"staff_account_display_name" `
GasBasicDisplayName string ` gorm:"column:gas_basic_display_name" json:"gas_basic_display_name" `
}
// ticketDetailQuery 在当前气站可见范围内解析关系名称,已失效关系仍保留可信的历史名称。
func ticketDetailQuery ( gasID uint64 ) * gorm . DB {
return ticketDetailQueryWithDB ( impl . DBService , gasID )
}
// ticketDetailQueryWithDB 构造可测试的详情名称查询,不改变通用列表查询。
func ticketDetailQueryWithDB ( databaseService * gorm . DB , gasID uint64 ) * gorm . DB {
return ticketQueryWithDB ( databaseService , gasID ) .
Select ( ` cs_ticket . * ,
2026-09-13 00:57:32 +08:00
CASE WHEN cs_ticket . gasorder_contract_id < > 0 AND cs_ticket . category = ' contract_change ' AND cs_ticket . ticket_status IN ( 32 , 11 ) THEN 1 ELSE 0 END AS contract_request_open ,
2026-08-22 10:48:36 +08:00
COALESCE ( NULLIF ( ticket_user . name , ' ' ) , NULLIF ( ticket_user . real_name , ' ' ) , NULLIF ( ticket_user . username , ' ' ) , ' 用户记录已失效 ' ) AS user_account_display_name ,
CASE WHEN cs_ticket . delivery_basic_id = 0 THEN ' ' ELSE COALESCE ( NULLIF ( ticket_delivery . name , ' ' ) , ' 配送点记录已失效 ' ) END AS delivery_basic_display_name ,
CASE WHEN cs_ticket . staff_account_id = 0 THEN ' ' ELSE COALESCE ( NULLIF ( ticket_staff . name , ' ' ) , NULLIF ( ticket_staff . username , ' ' ) , ' 工作人员记录已失效 ' ) END AS staff_account_display_name ,
CASE WHEN cs_ticket . gas_basic_id = 0 THEN ' ' ELSE COALESCE ( NULLIF ( ticket_gas . name , ' ' ) , ' 气站记录已失效 ' ) END AS gas_basic_display_name ` ) .
Joins ( "LEFT JOIN user_account AS ticket_user ON ticket_user.id = cs_ticket.user_account_id" ) .
Joins ( "LEFT JOIN delivery_basic AS ticket_delivery ON ticket_delivery.id = cs_ticket.delivery_basic_id" ) .
Joins ( "LEFT JOIN staff_account AS ticket_staff ON ticket_staff.id = cs_ticket.staff_account_id" ) .
Joins ( "LEFT JOIN gas_basic AS ticket_gas ON ticket_gas.id = cs_ticket.gas_basic_id" )
}
2026-07-30 14:16:58 +08:00
func ListTicket ( ctx * gin . Context ) {
station , ok := currentGas ( ctx )
if ! ok {
return
}
listScoped ( ctx , & models . CsTicket { } , ticketQuery ( station . ID ) , "cs_ticket.created_at desc" )
}
func GetTicket ( ctx * gin . Context ) {
station , ok := currentGas ( ctx )
if ! ok {
return
}
2026-08-22 10:48:36 +08:00
var ticket ticketDetailDisplay
respondScopedRecord ( ctx , ticketDetailQuery ( station . ID ) . Where ( "cs_ticket.identity = ?" , ctx . Param ( "identity" ) ) , & ticket )
2026-07-30 14:16:58 +08:00
}
type ticketRequest struct {
UserAccountIdentity string ` json:"user_account_identity" binding:"required" `
TicketNo string ` json:"ticket_no" binding:"required,max=64" `
Category string ` json:"category" binding:"required,max=64" `
Priority string ` json:"priority" binding:"required,max=16" `
}
func CreateTicket ( ctx * gin . Context ) {
station , ok := currentGas ( ctx )
if ! ok {
return
}
var request ticketRequest
2026-08-22 10:12:47 +08:00
if err := ctx . ShouldBindJSON ( & request ) ; err != nil ||
! isSupportedTicketCategory ( request . Category ) ||
! isSupportedTicketPriority ( request . Priority ) {
2026-07-30 14:16:58 +08:00
infra . Response . Error ( ctx , errcode . ErrInvalidArgument )
return
}
user , _ , ok := requireUser ( ctx , request . UserAccountIdentity , station . ID )
if ! ok {
return
}
ticket := models . CsTicket {
Entity : common . NewEntity ( common . StatusEnable ) , TicketStatus : common . StatusOpen ,
TicketNo : request . TicketNo , UserAccountID : user . ID , Category : request . Category , Priority : request . Priority ,
}
if err := impl . DBService . Create ( & ticket ) . Error ; err != nil {
infra . Response . Error ( ctx , err )
return
}
common . RespondCreatedResource ( ctx , ticket )
}
func UpdateTicket ( ctx * gin . Context ) {
station , ok := currentGas ( ctx )
if ! ok {
return
}
var ticket models . CsTicket
if err := ticketQuery ( station . ID ) . Where ( "cs_ticket.identity = ?" , ctx . Param ( "identity" ) ) . First ( & ticket ) . Error ; err != nil {
common . RespondRecordError ( ctx , err )
return
}
var request ticketRequest
2026-08-22 10:12:47 +08:00
if err := ctx . ShouldBindJSON ( & request ) ; err != nil ||
! isSupportedTicketCategory ( request . Category ) ||
! isSupportedTicketPriority ( request . Priority ) {
2026-07-30 14:16:58 +08:00
infra . Response . Error ( ctx , errcode . ErrInvalidArgument )
return
}
user , _ , ok := requireUser ( ctx , request . UserAccountIdentity , station . ID )
if ! ok {
return
}
2026-09-13 00:57:32 +08:00
if ticket . GasorderContractID != 0 && ( user . ID != ticket . UserAccountID || request . Category != "contract_change" ) {
infra . Response . Error ( ctx , errcode . ErrInvalidArgument )
return
}
2026-07-30 14:16:58 +08:00
if err := impl . DBService . Model ( & ticket ) . Updates ( map [ string ] any {
"user_account_id" : user . ID , "ticket_no" : request . TicketNo , "category" : request . Category , "priority" : request . Priority ,
} ) . Error ; err != nil {
infra . Response . Error ( ctx , err )
return
}
infra . Response . Success ( ctx , gin . H { "updated" : true } )
}
func UpdateTicketStatus ( ctx * gin . Context ) {
station , ok := currentGas ( ctx )
if ! ok {
return
}
var count int64
if err := ticketQuery ( station . ID ) . Where ( "cs_ticket.identity = ?" , ctx . Param ( "identity" ) ) . Count ( & count ) . Error ; err != nil || count != 1 {
infra . Response . Error ( ctx , errcode . ErrInvalidArgument )
return
}
common . UpdateRecordStatus ( ctx , & models . CsTicket { } )
}
func ArchiveTicket ( ctx * gin . Context ) {
station , ok := currentGas ( ctx )
if ! ok {
return
}
var count int64
if err := ticketQuery ( station . ID ) . Where ( "cs_ticket.identity = ?" , ctx . Param ( "identity" ) ) . Count ( & count ) . Error ; err != nil || count != 1 {
infra . Response . Error ( ctx , errcode . ErrInvalidArgument )
return
}
common . ArchiveRecord ( ctx , & models . CsTicket { } )
}