Files
ops2/backend/my_work/routers/apiCustomer.go
T

513 lines
15 KiB
Go

package routers
import (
"slices"
"time"
"ops/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
var (
customerUserGroup TabUserGroups
customerAdmins []uint
)
// TabCustomer 客户主表
type TabCustomer struct {
ID uint `gorm:"primarykey" json:"id"`
FirstName string `gorm:"size:100;comment:名" json:"first_name"`
LastName string `gorm:"size:100;comment:姓" json:"last_name"`
Title string `gorm:"size:20;comment:称呼:Unit/Mr/Ms" json:"title"`
CreatedBy uint `gorm:"not null;comment:创建人ID" json:"created_by"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime" json:"created_at"`
UpdatedAt *time.Time `gorm:"type:datetime;autoUpdateTime" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
}
// TabCustomerPhone 客户电话绑定表
type TabCustomerPhone struct {
ID uint `gorm:"primarykey" json:"id"`
CustomerID uint `gorm:"not null;index;comment:关联客户ID" json:"customer_id"`
Prefix string `gorm:"size:10;comment:地区前缀:86/852/853" json:"prefix"`
Phone string `gorm:"size:50;comment:电话号码" json:"phone"`
Label string `gorm:"size:20;comment:标签:mobile/work/home/other" json:"label"`
IsPrimary bool `gorm:"default:false;comment:是否主号码" json:"is_primary"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime" json:"created_at"`
}
// TabCustomerEmail 客户邮箱绑定表
type TabCustomerEmail struct {
ID uint `gorm:"primarykey" json:"id"`
CustomerID uint `gorm:"not null;index;comment:关联客户ID" json:"customer_id"`
Email string `gorm:"size:200;comment:邮箱地址" json:"email"`
Label string `gorm:"size:20;comment:标签:work/personal/other" json:"label"`
IsPrimary bool `gorm:"default:false;comment:是否主邮箱" json:"is_primary"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime" json:"created_at"`
}
// TabCustomerCompany 客户单位绑定表
type TabCustomerCompany struct {
ID uint `gorm:"primarykey" json:"id"`
CustomerID uint `gorm:"not null;index;comment:关联客户ID" json:"customer_id"`
CompanyName string `gorm:"size:200;comment:单位名称" json:"company_name"`
Department string `gorm:"size:100;comment:部门" json:"department"`
Position string `gorm:"size:100;comment:职位" json:"position"`
IsPrimary bool `gorm:"default:false;comment:是否主单位" json:"is_primary"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime" json:"created_at"`
}
// TabCustomerLog 客户操作日志
type TabCustomerLog struct {
ID uint `gorm:"primarykey"`
CustomerID uint `gorm:"not null;index;comment:关联客户ID"`
UserID uint `gorm:"not null;comment:操作人ID"`
ActionType string `gorm:"size:50;not null;comment:操作类型:create/update/delete/query"`
OldContent string `gorm:"type:text;comment:修改前内容(JSON)"`
NewContent string `gorm:"type:text;comment:修改后内容(JSON)"`
IP string `gorm:"size:50;comment:操作IP"`
Remark string `gorm:"size:500;comment:备注"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime;comment:操作时间"`
}
type From_customer_add struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Title string `json:"title"`
Phones []struct {
Prefix string `json:"prefix"`
Phone string `json:"phone"`
Label string `json:"label"`
IsPrimary bool `json:"is_primary"`
} `json:"phones"`
Emails []struct {
Email string `json:"email"`
Label string `json:"label"`
IsPrimary bool `json:"is_primary"`
} `json:"emails"`
Companies []struct {
CompanyName string `json:"company_name"`
Department string `json:"department"`
Position string `json:"position"`
IsPrimary bool `json:"is_primary"`
} `json:"companies"`
}
type From_customer_update struct {
ID uint `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Title string `json:"title"`
Phones []struct {
Prefix string `json:"prefix"`
Phone string `json:"phone"`
Label string `json:"label"`
IsPrimary bool `json:"is_primary"`
} `json:"phones"`
Emails []struct {
Email string `json:"email"`
Label string `json:"label"`
IsPrimary bool `json:"is_primary"`
} `json:"emails"`
Companies []struct {
CompanyName string `json:"company_name"`
Department string `json:"department"`
Position string `json:"position"`
IsPrimary bool `json:"is_primary"`
} `json:"companies"`
}
// CustomerUpdateAdminsCash 更新客户管理员缓存
func CustomerUpdateAdminsCash() {
customerAdmins = nil
customerAdmins = append(customerAdmins, 1) // id=1 系统管理员默认拥有所有权限
var binds []TabUserGroupBinds
models.DB.Where("group_id = ?", customerUserGroup.ID).Find(&binds)
for _, item := range binds {
if !slices.Contains(customerAdmins, item.UserID) {
customerAdmins = append(customerAdmins, item.UserID)
}
}
}
// customerAdminCheck 检查用户是否为客户管理员
func customerAdminCheck(userID uint) bool {
return slices.Contains(customerAdmins, userID)
}
// canModifyCustomer 判断是否有权限修改/删除客户(创建者或管理员)
func canModifyCustomer(userID, creatorUserID uint) bool {
if slices.Contains(customerAdmins, userID) {
return true
}
return userID == creatorUserID
}
// ApiCustomerInit 初始化客户模块
func ApiCustomerInit() {
// 自动创建 customer_admin 用户组
models.DB.Where("name = ?", "customer_admin").FirstOrCreate(&customerUserGroup, TabUserGroups{
Name: "customer_admin",
Type: "usergroup",
})
// 自动迁移客户相关表
models.DB.AutoMigrate(
&TabCustomer{},
&TabCustomerPhone{},
&TabCustomerEmail{},
&TabCustomerCompany{},
&TabCustomerLog{},
)
CustomerUpdateAdminsCash()
}
func ApiCustomer(r *gin.RouterGroup) {
// POST /add - 新增客户
r.POST("/add", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userNoLogin", nil)
return
}
//不再需要管理员权限就能新增
// if !customerAdminCheck(user.ID) {
// ReturnJson(ctx, "permission_denied", nil)
// return
// }
var params From_customer_add
if err := decodeJSON(data, &params); err != nil {
ReturnJson(ctx, "parameErr", nil)
return
}
// 创建客户
customer := TabCustomer{
FirstName: params.FirstName,
LastName: params.LastName,
Title: params.Title,
CreatedBy: user.ID,
}
if err := models.DB.Create(&customer).Error; err != nil {
ReturnJson(ctx, "dbErr", nil)
return
}
// 写入电话
for _, p := range params.Phones {
models.DB.Create(&TabCustomerPhone{
CustomerID: customer.ID,
Prefix: p.Prefix,
Phone: p.Phone,
Label: p.Label,
IsPrimary: p.IsPrimary,
})
}
// 写入邮箱
for _, e := range params.Emails {
models.DB.Create(&TabCustomerEmail{
CustomerID: customer.ID,
Email: e.Email,
Label: e.Label,
IsPrimary: e.IsPrimary,
})
}
// 写入单位
for _, c := range params.Companies {
models.DB.Create(&TabCustomerCompany{
CustomerID: customer.ID,
CompanyName: c.CompanyName,
Department: c.Department,
Position: c.Position,
IsPrimary: c.IsPrimary,
})
}
// 写日志
models.DB.Create(&TabCustomerLog{
CustomerID: customer.ID,
UserID: user.ID,
ActionType: "create",
IP: ctx.ClientIP(),
})
ReturnJson(ctx, "apiOK", gin.H{"id": customer.ID})
})
// POST /update - 编辑客户(创建者或管理员可操作)
r.POST("/update", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userNoLogin", nil)
return
}
var params From_customer_update
if err := decodeJSON(data, &params); err != nil {
ReturnJson(ctx, "parameErr", nil)
return
}
if params.ID == 0 {
ReturnJson(ctx, "parameErr", nil)
return
}
// 查找客户
var customer TabCustomer
if err := models.DB.Unscoped().First(&customer, params.ID).Error; err != nil {
ReturnJson(ctx, "parameErr", nil)
return
}
// 权限校验:只有创建者或管理员可以修改
if !canModifyCustomer(user.ID, customer.CreatedBy) {
ReturnJson(ctx, "no_permission", nil)
return
}
// 更新主表
models.DB.Model(&customer).Updates(map[string]interface{}{
"first_name": params.FirstName,
"last_name": params.LastName,
"title": params.Title,
})
// 重建绑定表:删除旧的,写入新的
models.DB.Where("customer_id = ?", customer.ID).Delete(&TabCustomerPhone{})
for _, p := range params.Phones {
models.DB.Create(&TabCustomerPhone{
CustomerID: customer.ID,
Prefix: p.Prefix,
Phone: p.Phone,
Label: p.Label,
IsPrimary: p.IsPrimary,
})
}
models.DB.Where("customer_id = ?", customer.ID).Delete(&TabCustomerEmail{})
for _, e := range params.Emails {
models.DB.Create(&TabCustomerEmail{
CustomerID: customer.ID,
Email: e.Email,
Label: e.Label,
IsPrimary: e.IsPrimary,
})
}
models.DB.Where("customer_id = ?", customer.ID).Delete(&TabCustomerCompany{})
for _, c := range params.Companies {
models.DB.Create(&TabCustomerCompany{
CustomerID: customer.ID,
CompanyName: c.CompanyName,
Department: c.Department,
Position: c.Position,
IsPrimary: c.IsPrimary,
})
}
// 写日志
models.DB.Create(&TabCustomerLog{
CustomerID: customer.ID,
UserID: user.ID,
ActionType: "update",
OldContent: "update", // 简化处理
IP: ctx.ClientIP(),
})
ReturnJson(ctx, "apiOK", nil)
})
// POST /delete - 软删除客户(创建者或管理员可操作)
r.POST("/delete", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userNoLogin", nil)
return
}
type Req struct {
ID uint `json:"id"`
}
var req Req
if err := decodeJSON(data, &req); err != nil || req.ID == 0 {
ReturnJson(ctx, "parameErr", nil)
return
}
var customer TabCustomer
if err := models.DB.First(&customer, req.ID).Error; err != nil {
ReturnJson(ctx, "parameErr", nil)
return
}
// 权限校验:只有创建者或管理员可以删除
if !canModifyCustomer(user.ID, customer.CreatedBy) {
ReturnJson(ctx, "no_permission", nil)
return
}
models.DB.Delete(&customer)
// 写日志
models.DB.Create(&TabCustomerLog{
CustomerID: customer.ID,
UserID: user.ID,
ActionType: "delete",
IP: ctx.ClientIP(),
})
ReturnJson(ctx, "apiOK", nil)
})
// POST /list - 客户列表(登录用户可读)
r.POST("/list", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userNoLogin", nil)
return
}
type Req struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Search string `json:"search"`
}
var req Req
if err := decodeJSON(data, &req); err != nil {
req.Page = 1
req.PageSize = 20
}
if req.Page < 1 {
req.Page = 1
}
if req.PageSize < 1 || req.PageSize > 100 {
req.PageSize = 20
}
offset := (req.Page - 1) * req.PageSize
// 子查询:获取每个客户的主电话/主邮箱/主单位用于搜索和展示
query := models.DB.Model(&TabCustomer{})
if req.Search != "" {
search := "%" + req.Search + "%"
query = query.Where(
models.DB.Where("first_name LIKE ? OR last_name LIKE ?", search, search).
Or("id IN (?)", models.DB.Table("tab_customer_phones").Select("customer_id").Where("phone LIKE ?", search)).
Or("id IN (?)", models.DB.Table("tab_customer_emails").Select("customer_id").Where("email LIKE ?", search)).
Or("id IN (?)", models.DB.Table("tab_customer_companies").Select("customer_id").Where("company_name LIKE ?", search)),
)
}
var total int64
query.Count(&total)
var customers []TabCustomer
query.Order("id DESC").Offset(offset).Limit(req.PageSize).Find(&customers)
// 组装列表数据(含主电话/主邮箱/主单位 + 编辑权限)
type CustomerListItem struct {
TabCustomer
PrimaryPhone string `json:"primary_phone"`
PrimaryEmail string `json:"primary_email"`
PrimaryCompany string `json:"primary_company"`
Edit bool `json:"edit"`
}
var list []CustomerListItem
for _, c := range customers {
item := CustomerListItem{
TabCustomer: c,
Edit: canModifyCustomer(user.ID, c.CreatedBy),
}
var phone TabCustomerPhone
if err := models.DB.Where("customer_id = ? AND is_primary = ?", c.ID, true).First(&phone).Error; err == nil {
item.PrimaryPhone = phone.Phone
} else if err := models.DB.Where("customer_id = ?", c.ID).First(&phone).Error; err == nil {
item.PrimaryPhone = phone.Phone
}
var email TabCustomerEmail
if err := models.DB.Where("customer_id = ? AND is_primary = ?", c.ID, true).First(&email).Error; err == nil {
item.PrimaryEmail = email.Email
} else if err := models.DB.Where("customer_id = ?", c.ID).First(&email).Error; err == nil {
item.PrimaryEmail = email.Email
}
var company TabCustomerCompany
if err := models.DB.Where("customer_id = ? AND is_primary = ?", c.ID, true).First(&company).Error; err == nil {
item.PrimaryCompany = company.CompanyName
} else if err := models.DB.Where("customer_id = ?", c.ID).First(&company).Error; err == nil {
item.PrimaryCompany = company.CompanyName
}
list = append(list, item)
}
ReturnJson(ctx, "apiOK", gin.H{
"customers": list,
"total": total,
"page": req.Page,
"page_size": req.PageSize,
})
})
// POST /get - 获取客户详情(登录用户可读)
r.POST("/get", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if !isAuth {
ReturnJson(ctx, "userNoLogin", nil)
return
}
type Req struct {
ID uint `json:"id"`
}
var req Req
if err := decodeJSON(data, &req); err != nil || req.ID == 0 {
ReturnJson(ctx, "parameErr", nil)
return
}
var customer TabCustomer
if err := models.DB.First(&customer, req.ID).Error; err != nil {
ReturnJson(ctx, "parameErr", nil)
return
}
// 获取电话列表
var phones []TabCustomerPhone
models.DB.Where("customer_id = ?", req.ID).Find(&phones)
// 获取邮箱列表
var emails []TabCustomerEmail
models.DB.Where("customer_id = ?", req.ID).Find(&emails)
// 获取单位列表
var companies []TabCustomerCompany
models.DB.Where("customer_id = ?", req.ID).Find(&companies)
// 写查询日志
// models.DB.Create(&TabCustomerLog{
// CustomerID: req.ID,
// ActionType: "query",
// IP: ctx.ClientIP(),
// })
ReturnJson(ctx, "apiOK", gin.H{
"customer": customer,
"phones": phones,
"emails": emails,
"companies": companies,
"canModify": canModifyCustomer(user.ID, customer.CreatedBy),
})
})
}