1.新增短信模版相关接口;
This commit is contained in:
parent
5a618d8c95
commit
fd3414f7c7
File diff suppressed because it is too large
Load Diff
|
@ -10,6 +10,7 @@ const (
|
||||||
ShowCount = 100 // 前端展示手机号数量
|
ShowCount = 100 // 前端展示手机号数量
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SmsTask 短信下行记录
|
||||||
type SmsTask struct {
|
type SmsTask struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ type SmsTask struct {
|
||||||
ScheduleTime *time.Time `gorm:"schedule_time"` // 计划发送时间(定时时间)
|
ScheduleTime *time.Time `gorm:"schedule_time"` // 计划发送时间(定时时间)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmsTaskBatch 短信批量任务记录
|
||||||
type SmsTaskBatch struct {
|
type SmsTaskBatch struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
|
@ -45,6 +47,7 @@ type SmsTaskBatch struct {
|
||||||
ScheduleTime *time.Time `gorm:"schedule_time"` // 计划发送时间(定时时间)
|
ScheduleTime *time.Time `gorm:"schedule_time"` // 计划发送时间(定时时间)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmsSendRecord 短信发送明细
|
||||||
type SmsSendRecord struct {
|
type SmsSendRecord struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
|
@ -70,6 +73,7 @@ type SensitiveWord struct {
|
||||||
IsEnabled bool `json:"is_enabled" gorm:"default:1;column:is_enabled;comment:是否启用"`
|
IsEnabled bool `json:"is_enabled" gorm:"default:1;column:is_enabled;comment:是否启用"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmsUplinkLog 短信上行记录
|
||||||
type SmsUplinkLog struct {
|
type SmsUplinkLog struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
|
@ -78,6 +82,7 @@ type SmsUplinkLog struct {
|
||||||
BatchID string `gorm:"column:batch_id;type:varchar(64);not null" json:"batch_id"` // 下行短信批次 ID
|
BatchID string `gorm:"column:batch_id;type:varchar(64);not null" json:"batch_id"` // 下行短信批次 ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmsSignatureRealname 签名实名制
|
||||||
type SmsSignatureRealname struct {
|
type SmsSignatureRealname struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
|
@ -100,12 +105,21 @@ type SmsSignatureRealname struct {
|
||||||
IsActive int `json:"is_active"` // 是否有效(0 无效;1 有效)
|
IsActive int `json:"is_active"` // 是否有效(0 无效;1 有效)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmsContactCategory 通讯录分类
|
||||||
|
type SmsContactCategory struct {
|
||||||
|
models.Model
|
||||||
|
Name string `json:"name" gorm:"type:varchar(100);not null"`
|
||||||
|
ParentID uint64 `json:"parent_id" gorm:"default:0"`
|
||||||
|
Children []SmsContactCategory `json:"children,omitempty" gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
// SmsContact 通讯录
|
// SmsContact 通讯录
|
||||||
type SmsContact struct {
|
type SmsContact struct {
|
||||||
models.Model
|
models.Model
|
||||||
|
|
||||||
CooperativeNumber string `gorm:"column:cooperative_number"` // 合作商编号
|
CategoryID uint64 `json:"category_id" gorm:"default:0"`
|
||||||
CooperativeName string `gorm:"column:cooperative_name"` // 合作商名称
|
CooperativeNumber string `json:"cooperative_number"` // 合作商编号
|
||||||
|
CooperativeName string `json:"cooperative_name"` // 合作商名称
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PhoneNumber string `json:"phone_number"`
|
PhoneNumber string `json:"phone_number"`
|
||||||
Gender string `json:"gender"`
|
Gender string `json:"gender"`
|
||||||
|
@ -115,6 +129,75 @@ type SmsContact struct {
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmsPhraseCategory 短语分类
|
||||||
|
type SmsPhraseCategory struct {
|
||||||
|
models.Model
|
||||||
|
Name string `json:"name" gorm:"type:varchar(100);not null"`
|
||||||
|
ParentID uint64 `json:"parent_id" gorm:"default:0"`
|
||||||
|
Children []SmsPhraseCategory `json:"children,omitempty" gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsPhrase 通讯录-常用短语
|
||||||
|
type SmsPhrase struct {
|
||||||
|
models.Model
|
||||||
|
|
||||||
|
Content string `json:"content" gorm:"type:varchar(255);not null"`
|
||||||
|
CategoryID uint `json:"category_id" gorm:"default:0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsCommonNumber 常用号码列表
|
||||||
|
type SmsCommonNumber struct {
|
||||||
|
models.Model
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
PhoneNumbers string `json:"phone_numbers"` // 如:"12345678901,13322223333"
|
||||||
|
PhoneCount int `json:"phone_count" gorm:"-"` // 号码数量(从 phone_numbers 计算)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsBlackList 黑名单列表
|
||||||
|
type SmsBlackList struct {
|
||||||
|
models.Model
|
||||||
|
|
||||||
|
PhoneNumber string `json:"phone_number"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsTemplate 短信模版表
|
||||||
|
type SmsTemplate struct {
|
||||||
|
models.Model
|
||||||
|
|
||||||
|
CooperativeNumber string `gorm:"column:cooperative_number"` // 合作商编号
|
||||||
|
CooperativeName string `gorm:"column:cooperative_name"` // 合作商名称
|
||||||
|
Content string `json:"content"` // 模版内容(必填)
|
||||||
|
ExpireAt *time.Time `json:"expire_at"` // 到期时间(有效期)
|
||||||
|
Remark string `json:"remark"` // 备注
|
||||||
|
Status int `json:"status"` // 状态:0=审核中 1=正常 2=拒绝 3=过期
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactInput struct {
|
||||||
|
CategoryID []uint64 `json:"category_id" binding:"required"`
|
||||||
|
ID uint64 `json:"id"` // 联系人ID
|
||||||
|
Name string `json:"name"`
|
||||||
|
PhoneNumber string `json:"phone_number"`
|
||||||
|
Gender string `json:"gender"`
|
||||||
|
BirthdayStr string `json:"birthday"` // 先用 string 接收
|
||||||
|
Company string `json:"company"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditContactInput struct {
|
||||||
|
CategoryID uint64 `json:"category_id" binding:"required"`
|
||||||
|
ID uint64 `json:"id"` // 联系人ID
|
||||||
|
Name string `json:"name"`
|
||||||
|
PhoneNumber string `json:"phone_number"`
|
||||||
|
Gender string `json:"gender"`
|
||||||
|
BirthdayStr string `json:"birthday"` // 先用 string 接收
|
||||||
|
Company string `json:"company"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
type MassImportPhoneResp struct {
|
type MassImportPhoneResp struct {
|
||||||
List []string `json:"list"` // 加密后的数据
|
List []string `json:"list"` // 加密后的数据
|
||||||
ImportSerialNumber string `json:"import_serial_number"` // 导入excel返回的编号
|
ImportSerialNumber string `json:"import_serial_number"` // 导入excel返回的编号
|
||||||
|
@ -272,12 +355,40 @@ type SignatureRealnameQueryResp struct {
|
||||||
TotalPage int64 `json:"total_page"`
|
TotalPage int64 `json:"total_page"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AddContactCategoryReq struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
ParentID uint `json:"parent_id"` // 默认 0 表示顶级
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditContactCategoryReq struct {
|
||||||
|
ID uint `json:"id" binding:"required"`
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteContactCategoryRequest 删除通讯录分类请求
|
||||||
|
type DeleteContactCategoryRequest struct {
|
||||||
|
IDs []uint `json:"ids" binding:"required"` // 要删除的分类ID列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListContactCategoryRequest 查询通讯录分类树的请求
|
||||||
|
type ListContactCategoryRequest struct {
|
||||||
|
CategoryID uint64 `json:"category_id"` // 为空表示查询整个树
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsContactCategoryTree struct {
|
||||||
|
ID uint64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ParentID uint64 `json:"parent_id"`
|
||||||
|
Children []SmsContactCategoryTree `json:"children,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// ContactQuery 请求结构体
|
// ContactQuery 请求结构体
|
||||||
type ContactQuery struct {
|
type ContactQuery struct {
|
||||||
Name string `form:"name"` // 模糊搜索
|
CategoryID []uint64 `json:"category_id" binding:"required"`
|
||||||
PhoneNumber string `form:"phone_number"` // 模糊搜索
|
Name string `form:"name"` // 模糊搜索
|
||||||
Page int `form:"page"`
|
PhoneNumber string `form:"phone_number"` // 模糊搜索
|
||||||
PageSize int `form:"page_size"`
|
Page int `form:"page"`
|
||||||
|
PageSize int `form:"page_size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContactQueryResp 响应结构体
|
// ContactQueryResp 响应结构体
|
||||||
|
@ -292,3 +403,205 @@ type ContactQueryResp struct {
|
||||||
type ContactDeleteRequest struct {
|
type ContactDeleteRequest struct {
|
||||||
ContactIDs []uint `json:"contact_ids" binding:"required"` // 要删除的记录ID列表
|
ContactIDs []uint `json:"contact_ids" binding:"required"` // 要删除的记录ID列表
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExportContactsRequest 导出联系人请求参数
|
||||||
|
type ExportContactsRequest struct {
|
||||||
|
IDs []uint `json:"ids"` // 联系人ID列表(优先)
|
||||||
|
All bool `json:"all"` // 是否导出全部
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExportContactsResp struct {
|
||||||
|
ExportUrl string `json:"export_url"` // 下载链接
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddPhraseCategoryReq struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
ParentID uint `json:"parent_id"` // 默认 0 表示顶级
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditPhraseCategoryReq struct {
|
||||||
|
ID uint `json:"id" binding:"required"`
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePhraseCategoryRequest 删除短语分类请求
|
||||||
|
type DeletePhraseCategoryRequest struct {
|
||||||
|
IDs []uint `json:"ids" binding:"required"` // 要删除的分类ID列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsPhraseQuery 查询参数
|
||||||
|
type SmsPhraseQuery struct {
|
||||||
|
Content string `form:"content" binding:"required"` // 模糊搜索内容
|
||||||
|
CategoryID uint `form:"category_id"` // 所属分类ID
|
||||||
|
Page int `form:"page"`
|
||||||
|
PageSize int `form:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsPhraseListResp struct {
|
||||||
|
List []SmsPhrase `json:"list"` // 短语列表
|
||||||
|
Total int64 `json:"total"` // 总条数
|
||||||
|
Page int `json:"page"` // 当前页
|
||||||
|
PageSize int `json:"page_size"` // 每页大小
|
||||||
|
TotalPage int64 `json:"total_page"` // 总页数
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsPhraseAddOrEdit 新增或编辑参数
|
||||||
|
type SmsPhraseAddOrEdit struct {
|
||||||
|
ID uint `json:"id"` // 编辑时用
|
||||||
|
Content string `json:"content" binding:"required"`
|
||||||
|
CategoryID uint `json:"category_id" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsPhraseBatchDeleteReq 批量删除
|
||||||
|
type SmsPhraseBatchDeleteReq struct {
|
||||||
|
IDs []uint `json:"ids" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPhraseCategoryRequest 查询短语分类树的请求
|
||||||
|
type ListPhraseCategoryRequest struct {
|
||||||
|
CategoryID uint64 `json:"category_id"` // 为空表示查询整个树
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsPhraseCategoryTree struct {
|
||||||
|
ID uint64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ParentID uint64 `json:"parent_id"`
|
||||||
|
Children []SmsPhraseCategoryTree `json:"children,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsCommonNumberQuery 查询
|
||||||
|
type SmsCommonNumberQuery struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsCommonNumberAddReq 添加常用号码请求结构体
|
||||||
|
type SmsCommonNumberAddReq struct {
|
||||||
|
Name string `json:"name" binding:"required"` // 名称
|
||||||
|
PhoneList []string `json:"phone_list" binding:"required"` // 号码列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsCommonNumberAppendReq 追加常用号码请求结构体
|
||||||
|
type SmsCommonNumberAppendReq struct {
|
||||||
|
ID int64 `json:"id" binding:"required"` // 记录ID
|
||||||
|
PhoneList []string `json:"phone_list" binding:"required"` // 需要追加的号码列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsCommonNumberDetailReq 常用号码详情
|
||||||
|
type SmsCommonNumberDetailReq struct {
|
||||||
|
ID int64 `json:"id" binding:"required"` // 记录ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsCommonNumberDeleteReq 删除
|
||||||
|
type SmsCommonNumberDeleteReq struct {
|
||||||
|
Ids []uint64 `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsCommonNumberExportReq 导出
|
||||||
|
type SmsCommonNumberExportReq struct {
|
||||||
|
All bool `json:"all"` // 是否导出全部
|
||||||
|
Ids []uint64 `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsCommonNumberExportResp struct {
|
||||||
|
ExportUrl string `json:"export_url"` // 下载链接
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmsCommonNumberListResp struct {
|
||||||
|
List []SmsCommonNumber `json:"list"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
TotalPage int64 `json:"totalPage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlacklistAddReq struct {
|
||||||
|
PhoneList []string `json:"phone_list" binding:"required"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportBlacklistRequest 导出黑名单请求
|
||||||
|
type ExportBlacklistRequest struct {
|
||||||
|
All bool `json:"all"` // 是否导出全部
|
||||||
|
Ids []string `json:"ids"` // 指定导出的手机号ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportBlacklistResp 导出响应
|
||||||
|
type ExportBlacklistResp struct {
|
||||||
|
ExportUrl string `json:"export_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlacklistQuery struct {
|
||||||
|
PhoneNumber string `json:"phone_number"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlacklistListResp struct {
|
||||||
|
List []SmsBlackList `json:"list"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
TotalPage int64 `json:"total_page"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsBlacklistBatchDeleteReq 批量删除黑名单请求
|
||||||
|
type SmsBlacklistBatchDeleteReq struct {
|
||||||
|
IDs []uint `json:"ids"` // 黑名单记录ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsTemplateQuery 短信模版查询参数
|
||||||
|
type SmsTemplateQuery struct {
|
||||||
|
Content string `json:"content"` // 模版内容模糊查询
|
||||||
|
Status int `json:"status"` // 模版状态(0=待审核,1=正常,2=审核拒绝,3=已过期)
|
||||||
|
CreateStart time.Time `json:"create_start"` // 创建时间起
|
||||||
|
CreateEnd time.Time `json:"create_end"` // 创建时间止
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsTemplateListResp 返回模版列表结构
|
||||||
|
type SmsTemplateListResp struct {
|
||||||
|
List []SmsTemplate `json:"list"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
TotalPage int64 `json:"total_page"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsTemplateExportReq 短信模版导出请求
|
||||||
|
type SmsTemplateExportReq struct {
|
||||||
|
Ids []uint `json:"ids"` // 选择的模版 ID 列表
|
||||||
|
All bool `json:"all"` // 是否导出全部
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportTemplateResp 导出结果
|
||||||
|
type ExportTemplateResp struct {
|
||||||
|
ExportUrl string `json:"export_url"` // 下载地址
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApproveTemplateRequest struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Status int `json:"status"` // 1 正常, 2 审核不通过
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsTemplateUpdateRequest 修改短信模版请求
|
||||||
|
type SmsTemplateUpdateRequest struct {
|
||||||
|
ID uint `json:"id" binding:"required"`
|
||||||
|
Content string `json:"content" binding:"required"`
|
||||||
|
ExpireAt *time.Time `json:"expire_at"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIdsRequest 批量删除模版时使用
|
||||||
|
type DeleteIdsRequest struct {
|
||||||
|
IDs []uint `json:"ids" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsTemplateCreateRequest 创建短信模版请求
|
||||||
|
type SmsTemplateCreateRequest struct {
|
||||||
|
Content string `json:"content" binding:"required"` // 模版内容
|
||||||
|
ExpireAt *time.Time `json:"expire_at"` // 到期时间
|
||||||
|
Remark string `json:"remark"` // 备注
|
||||||
|
}
|
||||||
|
|
|
@ -39,9 +39,43 @@ func registerSmsManageRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMidd
|
||||||
sms.POST("/signature_realname/update", api.UpdateSignatureRealname) // 编辑实名签名
|
sms.POST("/signature_realname/update", api.UpdateSignatureRealname) // 编辑实名签名
|
||||||
sms.POST("/signature_realname/delete", api.DeleteSignatureRealname) // 批量删除签名实名记录
|
sms.POST("/signature_realname/delete", api.DeleteSignatureRealname) // 批量删除签名实名记录
|
||||||
|
|
||||||
sms.POST("/contacts/list", api.ListContacts) // 查询通讯录列表
|
sms.POST("/contacts_category/add", api.AddContactsCategory) // 新增通讯录分类节点
|
||||||
sms.POST("/contacts/add", api.AddContact) // 新增联系人
|
sms.POST("/contacts_category/edit", api.EditContactsCategory) // 编辑通讯录分类节点
|
||||||
sms.POST("/contacts/edit", api.EditContact) // 编辑联系人
|
sms.POST("/contacts_category/delete", api.DeleteContactsCategories) // 删除通讯录分类
|
||||||
sms.POST("/contacts/batch_delete", api.BulkDeleteContacts) // 批量删除联系人
|
sms.POST("/contacts_category/list", api.ListContactsCategories) // 查询通讯录分类列表(树形结构)
|
||||||
|
sms.POST("/contacts/list", api.ListContacts) // 查询通讯录列表
|
||||||
|
sms.POST("/contacts/add", api.AddContact) // 新增联系人
|
||||||
|
sms.POST("/contacts/edit", api.EditContact) // 编辑联系人
|
||||||
|
sms.POST("/contacts/batch_delete", api.BulkDeleteContacts) // 批量删除联系人
|
||||||
|
sms.POST("/contacts/import", api.BulkImportContacts) // 批量导入通讯录
|
||||||
|
sms.POST("/contacts/export", api.BulkExportContacts) // 批量导出通讯录
|
||||||
|
|
||||||
|
sms.POST("/phrase_category/add", api.AddPhraseCategory) // 新增短语分类节点
|
||||||
|
sms.POST("/phrase_category/edit", api.EditPhraseCategory) // 编辑短语分类节点
|
||||||
|
sms.POST("/phrase_category/delete", api.DeletePhraseCategories) // 删除常用短语分类
|
||||||
|
sms.POST("/phrase_category/list", api.ListPhraseCategories) // 查询常用短语分类列表(树形结构)
|
||||||
|
sms.POST("/phrase/add", api.AddPhrase) // 新增常用短语
|
||||||
|
sms.POST("/phrase/edit", api.EditPhrase) // 编辑常用短语
|
||||||
|
sms.POST("/phrase/delete", api.DeletePhrases) // 批量删除短语
|
||||||
|
sms.POST("/phrase/list", api.ListPhrases) // 查询常用短语列表
|
||||||
|
|
||||||
|
sms.POST("/common_number/list", api.ListCommonNumbers) // 常用号码列表
|
||||||
|
sms.POST("/common_number/add", api.AddCommonNumber) // 添加常用号码
|
||||||
|
sms.POST("/common_number/append", api.AppendCommonNumber) // 追加号码
|
||||||
|
sms.POST("/common_number/detail", api.CommonNumberDetail) // 常用号码详情
|
||||||
|
sms.POST("/common_number/delete", api.DeleteCommonNumbers) // 批量删除常用号码
|
||||||
|
sms.POST("/common_number/export", api.ExportCommonNumber) // 导出常用号码
|
||||||
|
|
||||||
|
sms.POST("/black_list/add", api.AddBlacklistNumber) // 添加黑名单
|
||||||
|
sms.POST("/black_list/list", api.ListBlacklist) // 黑名单列表
|
||||||
|
sms.POST("/black_list/export", api.ExportBlacklist) // 导出黑名单号码
|
||||||
|
sms.POST("/black_list/delete", api.DeleteBlacklist) // 批量删除黑名单
|
||||||
|
|
||||||
|
sms.POST("/template/create", api.CreateSmsTemplate) // 新增短信模版
|
||||||
|
sms.POST("/template/delete", api.DeleteSmsTemplates) // 批量删除短信模版
|
||||||
|
sms.POST("/template/update", api.UpdateSmsTemplate) // 修改短信模版
|
||||||
|
sms.POST("/template/approve", api.ApproveSmsTemplate) // 审核短信模版
|
||||||
|
sms.POST("/template/list", api.ListSmsTemplates) // 获取短信模版列表
|
||||||
|
sms.POST("/template/export", api.ExportSmsTemplate) // 导出短信模版
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package bus_service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/csv"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/go-admin-team/go-admin-core/logger"
|
"github.com/go-admin-team/go-admin-core/logger"
|
||||||
|
@ -11,7 +12,11 @@ import (
|
||||||
"go-admin/common/redisx"
|
"go-admin/common/redisx"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -811,6 +816,9 @@ func (s *SmsService) ListContacts(req bus_models.ContactQuery, db *gorm.DB) (bus
|
||||||
|
|
||||||
query := db.Model(&bus_models.SmsContact{})
|
query := db.Model(&bus_models.SmsContact{})
|
||||||
|
|
||||||
|
if len(req.CategoryID) != 0 {
|
||||||
|
query = query.Where("category_id IN ?", req.CategoryID)
|
||||||
|
}
|
||||||
if req.Name != "" {
|
if req.Name != "" {
|
||||||
query = query.Where("name LIKE ?", "%"+req.Name+"%")
|
query = query.Where("name LIKE ?", "%"+req.Name+"%")
|
||||||
}
|
}
|
||||||
|
@ -862,7 +870,7 @@ func (s *SmsService) AddContact(contact bus_models.SmsContact, db *gorm.DB) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditContact 编辑联系人
|
// EditContact 编辑联系人
|
||||||
func (s *SmsService) EditContact(id string, contact bus_models.SmsContact, db *gorm.DB) error {
|
func (s *SmsService) EditContact(id uint64, contact bus_models.SmsContact, db *gorm.DB) error {
|
||||||
var existingContact bus_models.SmsContact
|
var existingContact bus_models.SmsContact
|
||||||
if err := db.Where("id = ?", id).First(&existingContact).Error; err != nil {
|
if err := db.Where("id = ?", id).First(&existingContact).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -886,3 +894,847 @@ func (s *SmsService) EditContact(id string, contact bus_models.SmsContact, db *g
|
||||||
func (s *SmsService) BulkDeleteContacts(req bus_models.ContactDeleteRequest, db *gorm.DB) error {
|
func (s *SmsService) BulkDeleteContacts(req bus_models.ContactDeleteRequest, db *gorm.DB) error {
|
||||||
return db.Where("id IN (?)", req.ContactIDs).Delete(&bus_models.SmsContact{}).Error
|
return db.Where("id IN (?)", req.ContactIDs).Delete(&bus_models.SmsContact{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImportContactsFromExcel 从Excel导入联系人(含格式校验)
|
||||||
|
func (s *SmsService) ImportContactsFromExcel(r io.Reader, db *gorm.DB, coopNum, coopName string, categoryId uint64) error {
|
||||||
|
excelFile, err := excelize.OpenReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("解析Excel失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := excelFile.GetRows("Sheet1")
|
||||||
|
if err != nil || len(rows) < 2 {
|
||||||
|
return errors.New("excel格式错误或无有效数据")
|
||||||
|
}
|
||||||
|
|
||||||
|
var contacts []bus_models.SmsContact
|
||||||
|
for i, row := range rows {
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(row) < 7 {
|
||||||
|
// 如果一行少于7个字段,补充缺失字段为空字符串或默认值
|
||||||
|
for len(row) < 7 {
|
||||||
|
row = append(row, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
phone := strings.TrimSpace(row[3])
|
||||||
|
if !isValidPhoneNumber(phone) {
|
||||||
|
return fmt.Errorf("第 %d 行手机号码格式不正确: %s", i+1, phone)
|
||||||
|
}
|
||||||
|
|
||||||
|
birthday, err := parseExcelDate(row[4])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("第 %d 行生日格式不正确,应为YYYY-MM-DD: %s", i+1, row[4])
|
||||||
|
}
|
||||||
|
|
||||||
|
contacts = append(contacts, bus_models.SmsContact{
|
||||||
|
CategoryID: categoryId,
|
||||||
|
CooperativeNumber: coopNum,
|
||||||
|
CooperativeName: coopName,
|
||||||
|
Name: strings.TrimSpace(row[0]),
|
||||||
|
Company: strings.TrimSpace(row[1]),
|
||||||
|
Gender: strings.TrimSpace(row[2]),
|
||||||
|
PhoneNumber: phone,
|
||||||
|
Birthday: birthday,
|
||||||
|
Address: strings.TrimSpace(row[5]),
|
||||||
|
Remark: strings.TrimSpace(row[6]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(contacts) == 0 {
|
||||||
|
return errors.New("无有效联系人记录")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Create(&contacts).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("导入失败:" + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidPhoneNumber(phone string) bool {
|
||||||
|
match, _ := regexp.MatchString(`^1\d{10}$`, phone)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseExcelDate(dateStr string) (*time.Time, error) {
|
||||||
|
if dateStr == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
layouts := []string{"2006-01-02", "2006/01/02"}
|
||||||
|
for _, layout := range layouts {
|
||||||
|
if t, err := time.Parse(layout, dateStr); err == nil {
|
||||||
|
return &t, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("日期格式不正确")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) ExportContactsToExcel(db *gorm.DB, req bus_models.ExportContactsRequest) (string, error) {
|
||||||
|
var contacts []bus_models.SmsContact
|
||||||
|
query := db.Order("id desc")
|
||||||
|
|
||||||
|
if !req.All {
|
||||||
|
if len(req.IDs) == 0 {
|
||||||
|
return "", fmt.Errorf("未传入ID且未选择导出全部")
|
||||||
|
}
|
||||||
|
query = query.Where("id IN ?", req.IDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := query.Find(&contacts).Error; err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(contacts) == 0 {
|
||||||
|
return "", fmt.Errorf("没有可导出的联系人")
|
||||||
|
}
|
||||||
|
|
||||||
|
file := excelize.NewFile()
|
||||||
|
sheet := "Sheet1"
|
||||||
|
headers := []string{"姓名", "公司", "性别", "手机号码", "生日", "地址", "备注"}
|
||||||
|
|
||||||
|
// 表头
|
||||||
|
for i, h := range headers {
|
||||||
|
col := string('A' + i)
|
||||||
|
file.SetCellValue(sheet, col+"1", h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容
|
||||||
|
for i, c := range contacts {
|
||||||
|
row := i + 2
|
||||||
|
file.SetCellValue(sheet, "A"+strconv.Itoa(row), c.Name)
|
||||||
|
file.SetCellValue(sheet, "B"+strconv.Itoa(row), c.Company)
|
||||||
|
file.SetCellValue(sheet, "C"+strconv.Itoa(row), c.Gender)
|
||||||
|
file.SetCellValue(sheet, "D"+strconv.Itoa(row), c.PhoneNumber)
|
||||||
|
if c.Birthday != nil {
|
||||||
|
file.SetCellValue(sheet, "E"+strconv.Itoa(row), c.Birthday.Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
file.SetCellValue(sheet, "F"+strconv.Itoa(row), c.Address)
|
||||||
|
file.SetCellValue(sheet, "G"+strconv.Itoa(row), c.Remark)
|
||||||
|
}
|
||||||
|
|
||||||
|
style, _ := file.NewStyle(&excelize.Style{
|
||||||
|
Alignment: &excelize.Alignment{
|
||||||
|
Horizontal: "center",
|
||||||
|
Vertical: "center",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
file.SetColWidth(sheet, "A", "G", 20)
|
||||||
|
file.SetCellStyle(sheet, "A1", fmt.Sprintf("G%d", len(contacts)+1), style)
|
||||||
|
|
||||||
|
// 保存文件
|
||||||
|
fileName := time.Now().Format("20060102150405") + "_导出通讯录.xlsx"
|
||||||
|
filePath := ExportFile + fileName
|
||||||
|
fileUrl := MiGuExportUrl + fileName
|
||||||
|
|
||||||
|
if err := file.SaveAs(filePath); err != nil {
|
||||||
|
logger.Errorf("导出通讯录失败: %v", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileUrl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) AddPhraseCategory(req bus_models.AddPhraseCategoryReq, db *gorm.DB) error {
|
||||||
|
return db.Create(&bus_models.SmsPhraseCategory{
|
||||||
|
Name: req.Name,
|
||||||
|
ParentID: uint64(req.ParentID),
|
||||||
|
}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) EditPhraseCategory(req bus_models.EditPhraseCategoryReq, db *gorm.DB) error {
|
||||||
|
return db.Model(&bus_models.SmsPhraseCategory{}).
|
||||||
|
Where("id = ?", req.ID).
|
||||||
|
Update("name", req.Name).
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) DeletePhraseCategories(ids []uint, db *gorm.DB) error {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return errors.New("未指定需要删除的分类")
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
if err := db.Model(&bus_models.SmsPhraseCategory{}).Count(&total).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归找出所有要删除的 ID(含子分类)
|
||||||
|
var allIDs []uint
|
||||||
|
var walk func(uint)
|
||||||
|
walk = func(parentID uint) {
|
||||||
|
var children []bus_models.SmsPhraseCategory
|
||||||
|
db.Where("parent_id = ?", parentID).Find(&children)
|
||||||
|
for _, child := range children {
|
||||||
|
allIDs = append(allIDs, uint(child.ID))
|
||||||
|
walk(uint(child.ID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
allIDs = append(allIDs, id)
|
||||||
|
walk(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保证删除后至少保留一个分类节点
|
||||||
|
if len(allIDs) >= int(total) {
|
||||||
|
return errors.New("无法删除所有分类节点,至少保留一个")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除短语
|
||||||
|
if err := db.Where("category_id IN ?", allIDs).Delete(&bus_models.SmsPhrase{}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除分类
|
||||||
|
return db.Where("id IN ?", allIDs).Delete(&bus_models.SmsPhraseCategory{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) ListPhrases(req bus_models.SmsPhraseQuery, db *gorm.DB) (bus_models.SmsPhraseListResp, error) {
|
||||||
|
var list []bus_models.SmsPhrase
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
query := db.Model(&bus_models.SmsPhrase{})
|
||||||
|
|
||||||
|
if req.Content != "" {
|
||||||
|
query = query.Where("content LIKE ?", "%"+req.Content+"%")
|
||||||
|
}
|
||||||
|
if req.CategoryID != 0 {
|
||||||
|
query = query.Where("category_id = ?", req.CategoryID)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := query.Count(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return bus_models.SmsPhraseListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理分页
|
||||||
|
page := req.Page
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
pageSize := req.PageSize
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
err = query.Order("id desc").
|
||||||
|
Offset((page - 1) * pageSize).
|
||||||
|
Limit(pageSize).
|
||||||
|
Find(&list).Error
|
||||||
|
if err != nil {
|
||||||
|
return bus_models.SmsPhraseListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bus_models.SmsPhraseListResp{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: page,
|
||||||
|
PageSize: pageSize,
|
||||||
|
TotalPage: (total + int64(pageSize) - 1) / int64(pageSize),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPhrase 新增
|
||||||
|
func (s *SmsService) AddPhrase(req bus_models.SmsPhraseAddOrEdit, db *gorm.DB) error {
|
||||||
|
phrase := bus_models.SmsPhrase{
|
||||||
|
Content: req.Content,
|
||||||
|
CategoryID: req.CategoryID,
|
||||||
|
}
|
||||||
|
return db.Create(&phrase).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditPhrase 编辑
|
||||||
|
func (s *SmsService) EditPhrase(req bus_models.SmsPhraseAddOrEdit, db *gorm.DB) error {
|
||||||
|
return db.Model(&bus_models.SmsPhrase{}).
|
||||||
|
Where("id = ?", req.ID).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"content": req.Content,
|
||||||
|
"category_id": req.CategoryID,
|
||||||
|
}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePhrases 批量删除
|
||||||
|
func (s *SmsService) DeletePhrases(ids []uint, db *gorm.DB) error {
|
||||||
|
return db.Where("id IN (?)", ids).Delete(&bus_models.SmsPhrase{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) GetPhraseCategoryTree(db *gorm.DB, parentID uint64) ([]bus_models.SmsPhraseCategoryTree, error) {
|
||||||
|
var categories []bus_models.SmsPhraseCategory
|
||||||
|
if err := db.Find(&categories).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 map[id]category
|
||||||
|
idMap := make(map[uint64]*bus_models.SmsPhraseCategoryTree)
|
||||||
|
for _, cat := range categories {
|
||||||
|
node := &bus_models.SmsPhraseCategoryTree{
|
||||||
|
ID: cat.ID,
|
||||||
|
Name: cat.Name,
|
||||||
|
ParentID: cat.ParentID,
|
||||||
|
}
|
||||||
|
idMap[cat.ID] = node
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建树结构
|
||||||
|
for _, node := range idMap {
|
||||||
|
if parent, ok := idMap[node.ParentID]; ok {
|
||||||
|
parent.Children = append(parent.Children, *node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回指定节点下的树
|
||||||
|
if parentID != 0 {
|
||||||
|
if root, ok := idMap[parentID]; ok {
|
||||||
|
return []bus_models.SmsPhraseCategoryTree{*root}, nil
|
||||||
|
}
|
||||||
|
return []bus_models.SmsPhraseCategoryTree{}, nil // 指定节点不存在
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则返回整棵树
|
||||||
|
var roots []bus_models.SmsPhraseCategoryTree
|
||||||
|
for _, node := range idMap {
|
||||||
|
if node.ParentID == 0 {
|
||||||
|
roots = append(roots, *node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) AddContactsCategory(req bus_models.AddContactCategoryReq, db *gorm.DB) error {
|
||||||
|
return db.Create(&bus_models.SmsContactCategory{
|
||||||
|
Name: req.Name,
|
||||||
|
ParentID: uint64(req.ParentID),
|
||||||
|
}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) EditContactsCategory(req bus_models.EditContactCategoryReq, db *gorm.DB) error {
|
||||||
|
return db.Model(&bus_models.SmsContactCategory{}).
|
||||||
|
Where("id = ?", req.ID).
|
||||||
|
Update("name", req.Name).
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) DeleteContactsCategories(ids []uint, db *gorm.DB) error {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return errors.New("未指定需要删除的分类")
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
if err := db.Model(&bus_models.SmsContactCategory{}).Count(&total).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归找出所有要删除的 ID(含子分类)
|
||||||
|
var allIDs []uint
|
||||||
|
var walk func(uint)
|
||||||
|
walk = func(parentID uint) {
|
||||||
|
var children []bus_models.SmsContactCategory
|
||||||
|
db.Where("parent_id = ?", parentID).Find(&children)
|
||||||
|
for _, child := range children {
|
||||||
|
allIDs = append(allIDs, uint(child.ID))
|
||||||
|
walk(uint(child.ID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
allIDs = append(allIDs, id)
|
||||||
|
walk(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保证删除后至少保留一个分类节点
|
||||||
|
if len(allIDs) >= int(total) {
|
||||||
|
return errors.New("无法删除所有分类节点,至少保留一个")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除短语
|
||||||
|
if err := db.Where("category_id IN ?", allIDs).Delete(&bus_models.SmsContact{}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除分类
|
||||||
|
return db.Where("id IN ?", allIDs).Delete(&bus_models.SmsContactCategory{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) GetContactsCategoryTree(db *gorm.DB, parentID uint64) ([]bus_models.SmsContactCategoryTree, error) {
|
||||||
|
var categories []bus_models.SmsContactCategory
|
||||||
|
if err := db.Find(&categories).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 map[id]category
|
||||||
|
idMap := make(map[uint64]*bus_models.SmsContactCategoryTree)
|
||||||
|
for _, cat := range categories {
|
||||||
|
node := &bus_models.SmsContactCategoryTree{
|
||||||
|
ID: cat.ID,
|
||||||
|
Name: cat.Name,
|
||||||
|
ParentID: cat.ParentID,
|
||||||
|
}
|
||||||
|
idMap[cat.ID] = node
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建树结构
|
||||||
|
for _, node := range idMap {
|
||||||
|
if parent, ok := idMap[node.ParentID]; ok {
|
||||||
|
parent.Children = append(parent.Children, *node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回指定节点下的树
|
||||||
|
if parentID != 0 {
|
||||||
|
if root, ok := idMap[parentID]; ok {
|
||||||
|
return []bus_models.SmsContactCategoryTree{*root}, nil
|
||||||
|
}
|
||||||
|
return []bus_models.SmsContactCategoryTree{}, nil // 指定节点不存在
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则返回整棵树
|
||||||
|
var roots []bus_models.SmsContactCategoryTree
|
||||||
|
for _, node := range idMap {
|
||||||
|
if node.ParentID == 0 {
|
||||||
|
roots = append(roots, *node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) ListCommonNumbers(req bus_models.SmsCommonNumberQuery, db *gorm.DB) (bus_models.SmsCommonNumberListResp, error) {
|
||||||
|
var dbList []bus_models.SmsCommonNumber
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
query := db.Model(&bus_models.SmsCommonNumber{})
|
||||||
|
if req.Name != "" {
|
||||||
|
query = query.Where("name LIKE ?", "%"+req.Name+"%")
|
||||||
|
}
|
||||||
|
err := query.Count(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return bus_models.SmsCommonNumberListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理分页
|
||||||
|
page := req.Page
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
pageSize := req.PageSize
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
err = query.Order("id desc").
|
||||||
|
Offset((page - 1) * pageSize).
|
||||||
|
Limit(pageSize).
|
||||||
|
Find(&dbList).Error
|
||||||
|
if err != nil {
|
||||||
|
return bus_models.SmsCommonNumberListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为返回结构体
|
||||||
|
var respList []bus_models.SmsCommonNumber
|
||||||
|
for _, item := range dbList {
|
||||||
|
respList = append(respList, bus_models.SmsCommonNumber{
|
||||||
|
Name: item.Name,
|
||||||
|
PhoneNumbers: item.PhoneNumbers,
|
||||||
|
PhoneCount: len(strings.Split(item.PhoneNumbers, ",")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPage := (total + int64(page) - 1) / int64(pageSize)
|
||||||
|
if totalPage < 1 {
|
||||||
|
totalPage = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return bus_models.SmsCommonNumberListResp{
|
||||||
|
List: respList,
|
||||||
|
Total: total,
|
||||||
|
Page: page,
|
||||||
|
PageSize: pageSize,
|
||||||
|
TotalPage: totalPage,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) AddCommonNumber(req bus_models.SmsCommonNumberAddReq, db *gorm.DB) error {
|
||||||
|
if req.Name == "" {
|
||||||
|
return errors.New("名称不能为空")
|
||||||
|
}
|
||||||
|
return db.Create(&bus_models.SmsCommonNumber{
|
||||||
|
Name: req.Name,
|
||||||
|
PhoneNumbers: strings.Join(req.PhoneList, ","),
|
||||||
|
}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendCommonNumber 向已有常用号码记录中追加号码(自动去重)
|
||||||
|
// 逻辑:获取原始号码列表 + 新号码列表 => 合并去重 => 更新保存
|
||||||
|
func (s *SmsService) AppendCommonNumber(req bus_models.SmsCommonNumberAppendReq, db *gorm.DB) error {
|
||||||
|
var record bus_models.SmsCommonNumber
|
||||||
|
err := db.Where("id = ?", req.ID).First(&record).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拆分原始号码
|
||||||
|
originalNumbers := strings.Split(record.PhoneNumbers, ",")
|
||||||
|
numberSet := make(map[string]struct{})
|
||||||
|
|
||||||
|
// 原号码去重填入 map
|
||||||
|
for _, num := range originalNumbers {
|
||||||
|
num = strings.TrimSpace(num)
|
||||||
|
if num != "" {
|
||||||
|
numberSet[num] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 新号码去重合并
|
||||||
|
for _, num := range req.PhoneList {
|
||||||
|
num = strings.TrimSpace(num)
|
||||||
|
if num != "" {
|
||||||
|
numberSet[num] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var deduplicated []string
|
||||||
|
for num := range numberSet {
|
||||||
|
deduplicated = append(deduplicated, num)
|
||||||
|
}
|
||||||
|
sort.Strings(deduplicated) // 方便前端对比、稳定输出顺序
|
||||||
|
|
||||||
|
record.PhoneNumbers = strings.Join(deduplicated, ",")
|
||||||
|
return db.Save(&record).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) GetCommonNumberDetail(id int64, db *gorm.DB) (bus_models.SmsCommonNumber, error) {
|
||||||
|
var record bus_models.SmsCommonNumber
|
||||||
|
err := db.First(&record, id).Error
|
||||||
|
return record, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) DeleteCommonNumbers(ids []uint64, db *gorm.DB) error {
|
||||||
|
return db.Where("id IN (?)", ids).Delete(&bus_models.SmsCommonNumber{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportCommonNumbers 导出多个常用号码名称下的所有号码到一个文件
|
||||||
|
func (s *SmsService) ExportCommonNumbers(req bus_models.SmsCommonNumberExportReq, db *gorm.DB) (string, error) {
|
||||||
|
var records []bus_models.SmsCommonNumber
|
||||||
|
|
||||||
|
query := db.Model(&bus_models.SmsCommonNumber{}).Order("id desc")
|
||||||
|
if !req.All {
|
||||||
|
if len(req.Ids) == 0 {
|
||||||
|
return "", fmt.Errorf("未传入号码且未选择导出全部")
|
||||||
|
}
|
||||||
|
query = query.Where("id IN ?", req.Ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := query.Find(&records).Error
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(records) == 0 {
|
||||||
|
return "", errors.New("未找到对应的常用号码数据")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建文件
|
||||||
|
fileName := time.Now().Format("20060102150405") + "_常用号码.xlsx"
|
||||||
|
filePath := ExportFile + fileName
|
||||||
|
fileUrl := MiGuExportUrl + fileName
|
||||||
|
|
||||||
|
file, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
writer := csv.NewWriter(file)
|
||||||
|
defer writer.Flush()
|
||||||
|
|
||||||
|
// 写数据
|
||||||
|
for _, record := range records {
|
||||||
|
numbers := strings.Split(record.PhoneNumbers, ",")
|
||||||
|
for _, number := range numbers {
|
||||||
|
trimmed := strings.TrimSpace(number)
|
||||||
|
if trimmed != "" {
|
||||||
|
writer.Write([]string{trimmed})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileUrl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) AddBlacklistNumber(req bus_models.BlacklistAddReq, db *gorm.DB) error {
|
||||||
|
if len(req.PhoneList) == 0 {
|
||||||
|
return fmt.Errorf("手机号列表不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
var records []bus_models.SmsBlackList
|
||||||
|
for _, phone := range req.PhoneList {
|
||||||
|
records = append(records, bus_models.SmsBlackList{
|
||||||
|
PhoneNumber: phone,
|
||||||
|
Remark: req.Remark,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.Create(&records).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) ListBlacklist(req bus_models.BlacklistQuery, db *gorm.DB) (bus_models.BlacklistListResp, error) {
|
||||||
|
var list []bus_models.SmsBlackList
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
query := db.Model(&bus_models.SmsBlackList{})
|
||||||
|
if req.PhoneNumber != "" {
|
||||||
|
query = query.Where("phone_number LIKE ?", "%"+req.PhoneNumber+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := query.Count(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return bus_models.BlacklistListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
page := req.Page
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
pageSize := req.PageSize
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
err = query.Order("id desc").
|
||||||
|
Offset((page - 1) * pageSize).
|
||||||
|
Limit(pageSize).
|
||||||
|
Find(&list).Error
|
||||||
|
if err != nil {
|
||||||
|
return bus_models.BlacklistListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPage := (total + int64(pageSize) - 1) / int64(pageSize)
|
||||||
|
if totalPage < 1 {
|
||||||
|
totalPage = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return bus_models.BlacklistListResp{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: page,
|
||||||
|
PageSize: pageSize,
|
||||||
|
TotalPage: totalPage,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) ExportBlacklistToExcel(db *gorm.DB, req bus_models.ExportBlacklistRequest) (string, error) {
|
||||||
|
var blacklists []bus_models.SmsBlackList
|
||||||
|
query := db.Model(&bus_models.SmsBlackList{}).Order("id desc")
|
||||||
|
|
||||||
|
if !req.All {
|
||||||
|
if len(req.Ids) == 0 {
|
||||||
|
return "", fmt.Errorf("未传入号码且未选择导出全部")
|
||||||
|
}
|
||||||
|
query = query.Where("id IN ?", req.Ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := query.Find(&blacklists).Error; err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(blacklists) == 0 {
|
||||||
|
return "", fmt.Errorf("没有可导出的黑名单记录")
|
||||||
|
}
|
||||||
|
|
||||||
|
file := excelize.NewFile()
|
||||||
|
sheet := "Sheet1"
|
||||||
|
headers := []string{"手机号", "备注", "创建时间"}
|
||||||
|
|
||||||
|
for i, h := range headers {
|
||||||
|
col := string('A' + i)
|
||||||
|
file.SetCellValue(sheet, col+"1", h)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, b := range blacklists {
|
||||||
|
row := strconv.Itoa(i + 2)
|
||||||
|
file.SetCellValue(sheet, "A"+row, b.PhoneNumber)
|
||||||
|
file.SetCellValue(sheet, "B"+row, b.Remark)
|
||||||
|
file.SetCellValue(sheet, "C"+row, b.CreatedAt.Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
|
|
||||||
|
style, _ := file.NewStyle(&excelize.Style{
|
||||||
|
Alignment: &excelize.Alignment{
|
||||||
|
Horizontal: "center",
|
||||||
|
Vertical: "center",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
file.SetColWidth(sheet, "A", "C", 20)
|
||||||
|
file.SetCellStyle(sheet, "A1", fmt.Sprintf("C%d", len(blacklists)+1), style)
|
||||||
|
|
||||||
|
fileName := time.Now().Format("20060102150405") + "_导出黑名单.xlsx"
|
||||||
|
filePath := ExportFile + fileName
|
||||||
|
fileUrl := MiGuExportUrl + fileName
|
||||||
|
|
||||||
|
if err := file.SaveAs(filePath); err != nil {
|
||||||
|
logger.Errorf("导出黑名单失败: %v", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileUrl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBlacklist 批量删除黑名单记录
|
||||||
|
func (s *SmsService) DeleteBlacklist(ids []uint, db *gorm.DB) error {
|
||||||
|
return db.Where("id IN ?", ids).Delete(&bus_models.SmsBlackList{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSmsTemplate 创建短信模版
|
||||||
|
func (s *SmsService) CreateSmsTemplate(data *bus_models.SmsTemplate, db *gorm.DB) error {
|
||||||
|
return db.Create(data).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSmsTemplates 批量删除短信模版
|
||||||
|
func (s *SmsService) DeleteSmsTemplates(ids []uint, db *gorm.DB) error {
|
||||||
|
return db.Delete(&bus_models.SmsTemplate{}, ids).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSmsTemplate 更新短信模版
|
||||||
|
func (s *SmsService) UpdateSmsTemplate(req *bus_models.SmsTemplateUpdateRequest, db *gorm.DB) error {
|
||||||
|
var tmpl bus_models.SmsTemplate
|
||||||
|
|
||||||
|
// 查找原始数据
|
||||||
|
if err := db.First(&tmpl, req.ID).Error; err != nil {
|
||||||
|
return fmt.Errorf("未找到指定的短信模版")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新模版内容
|
||||||
|
tmpl.Content = req.Content
|
||||||
|
|
||||||
|
// 更新到期时间(如果传入了新的时间)
|
||||||
|
if !req.ExpireAt.IsZero() {
|
||||||
|
tmpl.ExpireAt = req.ExpireAt
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新更新时间
|
||||||
|
tmpl.UpdatedAt = time.Now()
|
||||||
|
|
||||||
|
// 保存更新后的模版
|
||||||
|
return db.Save(&tmpl).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApproveSmsTemplate 审核短信模版
|
||||||
|
func (s *SmsService) ApproveSmsTemplate(id uint, status int, db *gorm.DB) error {
|
||||||
|
return db.Model(&bus_models.SmsTemplate{}).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Update("status", status).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) ListSmsTemplates(req bus_models.SmsTemplateQuery, db *gorm.DB) (bus_models.SmsTemplateListResp, error) {
|
||||||
|
var list []bus_models.SmsTemplate
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
query := db.Model(&bus_models.SmsTemplate{})
|
||||||
|
|
||||||
|
if req.Content != "" {
|
||||||
|
query = query.Where("content LIKE ?", "%"+req.Content+"%")
|
||||||
|
}
|
||||||
|
if req.Status != 0 {
|
||||||
|
query = query.Where("status = ?", req.Status)
|
||||||
|
}
|
||||||
|
if !req.CreateStart.IsZero() && !req.CreateEnd.IsZero() {
|
||||||
|
query = query.Where("created_at BETWEEN ? AND ?", req.CreateStart, req.CreateEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := query.Count(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return bus_models.SmsTemplateListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
page := req.Page
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
pageSize := req.PageSize
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
err = query.Order("id desc").
|
||||||
|
Offset((page - 1) * pageSize).
|
||||||
|
Limit(pageSize).
|
||||||
|
Find(&list).Error
|
||||||
|
if err != nil {
|
||||||
|
return bus_models.SmsTemplateListResp{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPage := (total + int64(pageSize) - 1) / int64(pageSize)
|
||||||
|
if totalPage < 1 {
|
||||||
|
totalPage = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return bus_models.SmsTemplateListResp{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: page,
|
||||||
|
PageSize: pageSize,
|
||||||
|
TotalPage: totalPage,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SmsService) ExportSmsTemplates(req bus_models.SmsTemplateExportReq, db *gorm.DB) (string, error) {
|
||||||
|
var templates []bus_models.SmsTemplate
|
||||||
|
|
||||||
|
query := db.Model(&bus_models.SmsTemplate{}).Order("id desc")
|
||||||
|
if !req.All {
|
||||||
|
if len(req.Ids) == 0 {
|
||||||
|
return "", fmt.Errorf("未传入模版且未选择导出全部")
|
||||||
|
}
|
||||||
|
query = query.Where("id IN ?", req.Ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := query.Find(&templates).Error
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(templates) == 0 {
|
||||||
|
return "", errors.New("未找到对应的短信模版数据")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成文件
|
||||||
|
fileName := time.Now().Format("20060102150405") + "_短信模版导出.csv"
|
||||||
|
filePath := ExportFile + fileName
|
||||||
|
fileUrl := MiGuExportUrl + fileName
|
||||||
|
|
||||||
|
file, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
writer := csv.NewWriter(file)
|
||||||
|
defer writer.Flush()
|
||||||
|
|
||||||
|
// 写表头
|
||||||
|
writer.Write([]string{"模版ID", "模版内容", "备注", "状态", "创建时间"})
|
||||||
|
|
||||||
|
// 写内容
|
||||||
|
for _, tmpl := range templates {
|
||||||
|
status := map[int]string{
|
||||||
|
0: "待审核",
|
||||||
|
1: "正常",
|
||||||
|
2: "审核拒绝",
|
||||||
|
3: "已过期",
|
||||||
|
}[tmpl.Status]
|
||||||
|
|
||||||
|
writer.Write([]string{
|
||||||
|
strconv.Itoa(int(tmpl.ID)),
|
||||||
|
tmpl.Content,
|
||||||
|
tmpl.Remark,
|
||||||
|
status,
|
||||||
|
tmpl.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileUrl, nil
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
9
tools/utils/utils.go
Normal file
9
tools/utils/utils.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
func IsValidPhone(phone string) bool {
|
||||||
|
// 简单验证:以1开头,后面10位数字
|
||||||
|
reg := regexp.MustCompile(`^1[0-9]{10}$`)
|
||||||
|
return reg.MatchString(phone)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user