工单可以关联客户
This commit is contained in:
@@ -1,11 +1,511 @@
|
||||
package routers
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
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 - 新增客户(需 customer_admin 权限)
|
||||
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, ¶ms); 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, ¶ms); 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),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -391,6 +391,8 @@ func ApiSysAdmin(r *gin.RouterGroup) {
|
||||
WorkOrderUpdateAdminsCash()
|
||||
case "warehouse_admin":
|
||||
WarehouseUpdateAdminsCash()
|
||||
case "customer_admin":
|
||||
CustomerUpdateAdminsCash()
|
||||
}
|
||||
|
||||
ReturnJson(ctx, "apiOK", nil)
|
||||
@@ -444,6 +446,8 @@ func ApiSysAdmin(r *gin.RouterGroup) {
|
||||
WorkOrderUpdateAdminsCash()
|
||||
case "warehouse_admin":
|
||||
WarehouseUpdateAdminsCash()
|
||||
case "customer_admin":
|
||||
CustomerUpdateAdminsCash()
|
||||
}
|
||||
|
||||
ReturnJson(ctx, "apiOK", nil)
|
||||
|
||||
@@ -114,7 +114,8 @@ func ApiWorkOrder(r *gin.RouterGroup) {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Photos []string `json:"photos"`
|
||||
ItemID *uint `json:"item_id"`
|
||||
ItemIDs []uint `json:"item_ids"`
|
||||
CustomerIDs []uint `json:"customer_ids"`
|
||||
}
|
||||
var from FromAdd
|
||||
if err := decodeJSON(data, &from); err != nil || from.Title == "" {
|
||||
@@ -149,13 +150,26 @@ func ApiWorkOrder(r *gin.RouterGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定物品
|
||||
if from.ItemID != nil && *from.ItemID > 0 {
|
||||
models.DB.Create(&TabWarehouseItemWorkOrderBind{
|
||||
ItemID: *from.ItemID,
|
||||
WorkOrderID: order.ID,
|
||||
CreatorID: user.ID,
|
||||
})
|
||||
// 绑定物品(支持多个)
|
||||
for _, itemID := range from.ItemIDs {
|
||||
if itemID > 0 {
|
||||
models.DB.Create(&TabWarehouseItemWorkOrderBind{
|
||||
ItemID: itemID,
|
||||
WorkOrderID: order.ID,
|
||||
CreatorID: user.ID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定客户(支持多个)
|
||||
for _, customerID := range from.CustomerIDs {
|
||||
if customerID > 0 {
|
||||
models.DB.Create(&TabWorkOrderCustomerBind{
|
||||
WorkOrderID: order.ID,
|
||||
CustomerID: customerID,
|
||||
CreatorID: user.ID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 写创建 commit
|
||||
@@ -190,17 +204,19 @@ func ApiWorkOrder(r *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
type FromUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Photos []string `json:"photos"`
|
||||
}
|
||||
var from FromUpdate
|
||||
if err := decodeJSON(data, &from); err != nil || from.ID == 0 || from.Title == "" {
|
||||
ReturnJson(ctx, "jsonErr", nil)
|
||||
return
|
||||
}
|
||||
type FromUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Photos []string `json:"photos"`
|
||||
ItemIDs []uint `json:"item_ids"`
|
||||
CustomerIDs []uint `json:"customer_ids"`
|
||||
}
|
||||
var from FromUpdate
|
||||
if err := decodeJSON(data, &from); err != nil || from.ID == 0 || from.Title == "" {
|
||||
ReturnJson(ctx, "jsonErr", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 校验图片哈希
|
||||
for _, hash := range from.Photos {
|
||||
@@ -240,6 +256,25 @@ func ApiWorkOrder(r *gin.RouterGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
// 重建物品关联绑定
|
||||
models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWarehouseItemWorkOrderBind{})
|
||||
for _, itemID := range from.ItemIDs {
|
||||
models.DB.Create(&TabWarehouseItemWorkOrderBind{
|
||||
WorkOrderID: from.ID,
|
||||
ItemID: itemID,
|
||||
})
|
||||
}
|
||||
|
||||
// 重建客户关联绑定
|
||||
models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWorkOrderCustomerBind{})
|
||||
for _, customerID := range from.CustomerIDs {
|
||||
models.DB.Create(&TabWorkOrderCustomerBind{
|
||||
WorkOrderID: from.ID,
|
||||
CustomerID: customerID,
|
||||
CreatorID: user.ID,
|
||||
})
|
||||
}
|
||||
|
||||
newContent, _ := json.Marshal(from)
|
||||
models.DB.Create(&TabWorkOrderLog{
|
||||
WorkOrderID: from.ID,
|
||||
@@ -414,16 +449,156 @@ func ApiWorkOrder(r *gin.RouterGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
// 关联客户
|
||||
type LinkedCustomer struct {
|
||||
ID uint `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
PrimaryPhone string `json:"primary_phone"`
|
||||
}
|
||||
var linkedCustomers []LinkedCustomer
|
||||
var customerBinds []TabWorkOrderCustomerBind
|
||||
models.DB.Where("work_order_id = ?", from.ID).Find(&customerBinds)
|
||||
if len(customerBinds) > 0 {
|
||||
var customerIDs []uint
|
||||
for _, b := range customerBinds {
|
||||
customerIDs = append(customerIDs, b.CustomerID)
|
||||
}
|
||||
var customers []TabCustomer
|
||||
models.DB.Where("id IN ?", customerIDs).Find(&customers)
|
||||
for _, c := range customers {
|
||||
item := LinkedCustomer{
|
||||
ID: c.ID,
|
||||
FirstName: c.FirstName,
|
||||
LastName: c.LastName,
|
||||
PrimaryPhone: "",
|
||||
}
|
||||
// 获取主电话
|
||||
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
|
||||
}
|
||||
linkedCustomers = append(linkedCustomers, item)
|
||||
}
|
||||
}
|
||||
|
||||
ReturnJson(ctx, "apiOK", gin.H{
|
||||
"order": order,
|
||||
"canModify": canModify,
|
||||
"canCommit": canCommit,
|
||||
"photos": files,
|
||||
"commits": commitsWithPhotos,
|
||||
"linkedItems": linkedItems,
|
||||
"order": order,
|
||||
"canModify": canModify,
|
||||
"canCommit": canCommit,
|
||||
"photos": files,
|
||||
"commits": commitsWithPhotos,
|
||||
"linkedItems": linkedItems,
|
||||
"linkedCustomers": linkedCustomers,
|
||||
})
|
||||
})
|
||||
|
||||
// 关联客户到工单
|
||||
r.POST("/link_customer", func(ctx *gin.Context) {
|
||||
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||
if !isAuth {
|
||||
ReturnJson(ctx, "userCookieError", nil)
|
||||
return
|
||||
}
|
||||
|
||||
type FromLinkCustomer struct {
|
||||
WorkOrderID uint `json:"work_order_id"`
|
||||
CustomerID uint `json:"customer_id"`
|
||||
}
|
||||
var from FromLinkCustomer
|
||||
if err := decodeJSON(data, &from); err != nil || from.WorkOrderID == 0 || from.CustomerID == 0 {
|
||||
ReturnJson(ctx, "parameErr", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查工单是否存在
|
||||
var order TabWorkOrder
|
||||
if err := models.DB.Where("id = ?", from.WorkOrderID).First(&order).Error; err != nil {
|
||||
ReturnJson(ctx, "order_not_found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查客户是否存在
|
||||
var customer TabCustomer
|
||||
if err := models.DB.Where("id = ?", from.CustomerID).First(&customer).Error; err != nil {
|
||||
ReturnJson(ctx, "customer_not_found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否已关联
|
||||
var existingBind TabWorkOrderCustomerBind
|
||||
if err := models.DB.Where("work_order_id = ? AND customer_id = ?", from.WorkOrderID, from.CustomerID).First(&existingBind).Error; err == nil {
|
||||
ReturnJson(ctx, "already_linked", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建关联
|
||||
if err := models.DB.Create(&TabWorkOrderCustomerBind{
|
||||
WorkOrderID: from.WorkOrderID,
|
||||
CustomerID: from.CustomerID,
|
||||
CreatorID: user.ID,
|
||||
}).Error; err != nil {
|
||||
ReturnJson(ctx, "dbErr", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 写操作日志
|
||||
models.DB.Create(&TabWorkOrderLog{
|
||||
WorkOrderID: from.WorkOrderID,
|
||||
UserID: user.ID,
|
||||
ActionType: "link_customer",
|
||||
NewContent: parsefmt.Sprintf("关联客户 ID: %d", from.CustomerID),
|
||||
IP: ctx.ClientIP(),
|
||||
})
|
||||
|
||||
ReturnJson(ctx, "apiOK", nil)
|
||||
})
|
||||
|
||||
// 解除工单与客户关联
|
||||
r.POST("/unlink_customer", func(ctx *gin.Context) {
|
||||
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||
if !isAuth {
|
||||
ReturnJson(ctx, "userCookieError", nil)
|
||||
return
|
||||
}
|
||||
|
||||
type FromUnlinkCustomer struct {
|
||||
WorkOrderID uint `json:"work_order_id"`
|
||||
CustomerID uint `json:"customer_id"`
|
||||
}
|
||||
var from FromUnlinkCustomer
|
||||
if err := decodeJSON(data, &from); err != nil || from.WorkOrderID == 0 || from.CustomerID == 0 {
|
||||
ReturnJson(ctx, "parameErr", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查工单是否存在
|
||||
var order TabWorkOrder
|
||||
if err := models.DB.Where("id = ?", from.WorkOrderID).First(&order).Error; err != nil {
|
||||
ReturnJson(ctx, "order_not_found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 删除关联
|
||||
if err := models.DB.Where("work_order_id = ? AND customer_id = ?", from.WorkOrderID, from.CustomerID).Delete(&TabWorkOrderCustomerBind{}).Error; err != nil {
|
||||
ReturnJson(ctx, "dbErr", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 写操作日志
|
||||
models.DB.Create(&TabWorkOrderLog{
|
||||
WorkOrderID: from.WorkOrderID,
|
||||
UserID: user.ID,
|
||||
ActionType: "unlink_customer",
|
||||
NewContent: parsefmt.Sprintf("解除关联客户 ID: %d", from.CustomerID),
|
||||
IP: ctx.ClientIP(),
|
||||
})
|
||||
|
||||
ReturnJson(ctx, "apiOK", nil)
|
||||
})
|
||||
|
||||
// 新增进度 commit(状态推进)
|
||||
r.POST("/commit", func(ctx *gin.Context) {
|
||||
isAuth, user, data := AuthenticationAuthority(ctx)
|
||||
|
||||
@@ -56,6 +56,15 @@ type TabWorkOrderPurchaseOrderBind struct {
|
||||
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
|
||||
}
|
||||
|
||||
// TabWorkOrderCustomerBind 工单与客户关联表
|
||||
type TabWorkOrderCustomerBind struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
WorkOrderID uint `gorm:"not null;index;comment:关联工单ID"`
|
||||
CustomerID uint `gorm:"not null;index;comment:关联客户ID"`
|
||||
CreatorID uint `gorm:"not null;comment:绑定人id"`
|
||||
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
|
||||
}
|
||||
|
||||
type TabWarehouseContainerFileBind struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
ContainerID uint `gorm:"not null;index;comment:关联容器id"`
|
||||
@@ -73,6 +82,7 @@ func BindsInit() {
|
||||
&TabWorkOrderFileBind{},
|
||||
&TabWorkOrderCommitFileBind{},
|
||||
&TabWorkOrderPurchaseOrderBind{},
|
||||
&TabWorkOrderCustomerBind{},
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user