From 35886fde4f9f2ebc644402103f3ea29a909fb455 Mon Sep 17 00:00:00 2001
From: zchengo <1933757688@qq.com>
Date: Tue, 24 Jan 2023 20:03:35 +0800
Subject: [PATCH] feat: mail config and mail send
---
server/api/config.go | 78 +++++++++++++
server/api/customer.go | 16 +++
server/common/mail.go | 28 ++++-
server/dao/config.go | 82 ++++++++++++++
server/initialize/router.go | 7 ++
server/models/common.go | 11 ++
server/models/config.go | 39 +++++++
server/models/customer.go | 7 ++
server/response/errcode.go | 7 ++
server/service/config.go | 63 +++++++++++
server/service/customer.go | 30 +++++
web/src/api/config.js | 37 ++++++
web/src/api/customer.js | 9 ++
web/src/router/index.js | 6 +
web/src/views/Config.vue | 217 ++++++++++++++++++++++++++++++++++++
web/src/views/Customer.vue | 92 ++++++++++++++-
web/src/views/Home.vue | 10 +-
17 files changed, 731 insertions(+), 8 deletions(-)
create mode 100644 server/api/config.go
create mode 100644 server/dao/config.go
create mode 100644 server/models/config.go
create mode 100644 server/service/config.go
create mode 100644 web/src/api/config.js
create mode 100644 web/src/views/Config.vue
diff --git a/server/api/config.go b/server/api/config.go
new file mode 100644
index 0000000..fac674d
--- /dev/null
+++ b/server/api/config.go
@@ -0,0 +1,78 @@
+package api
+
+import (
+ "crm/models"
+ "crm/response"
+ "crm/service"
+ "log"
+ "strconv"
+
+ "github.com/gin-gonic/gin"
+)
+
+type MailConfigApi struct {
+ mailConfigService *service.MailConfigService
+}
+
+func NewMailConfigApi() *MailConfigApi {
+ return &MailConfigApi{
+ mailConfigService: service.NewMailConfigService(),
+ }
+}
+
+// 保存邮件服务配置
+func (m *MailConfigApi) Save(context *gin.Context) {
+ var param models.MailConfigSaveParam
+ uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
+ err := context.ShouldBind(¶m)
+ if uid <= 0 || err != nil {
+ response.Result(response.ErrCodeParamInvalid, nil, context)
+ log.Println(err)
+ return
+ }
+ param.Creator = int64(uid)
+ errCode := m.mailConfigService.Save(¶m)
+ if errCode == 0 {
+ mailConfig, errCode := m.mailConfigService.GetInfo(int64(uid))
+ response.Result(errCode, mailConfig, context)
+ return
+ }
+ response.Result(errCode, nil, context)
+}
+
+// 开启或关闭邮件服务
+func (m *MailConfigApi) UpdateStatus(context *gin.Context) {
+ var param models.MailConfigStatusParam
+ uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
+ err := context.ShouldBind(¶m)
+ if uid <= 0 || err != nil {
+ response.Result(response.ErrCodeParamInvalid, nil, context)
+ log.Println(err)
+ return
+ }
+ param.Creator = int64(uid)
+ errCode := m.mailConfigService.UpdateStatus(¶m)
+ response.Result(errCode, nil, context)
+}
+
+// 获取邮件服务配置信息
+func (m *MailConfigApi) GetInfo(context *gin.Context) {
+ uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
+ if uid <= 0 {
+ response.Result(response.ErrCodeParamInvalid, nil, context)
+ return
+ }
+ mailConfig, errCode := m.mailConfigService.GetInfo(int64(uid))
+ response.Result(errCode, mailConfig, context)
+}
+
+// 检查邮件配置的有效性
+func (m *MailConfigApi) Check(context *gin.Context) {
+ uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
+ if uid <= 0 {
+ response.Result(response.ErrCodeParamInvalid, nil, context)
+ return
+ }
+ errCode := m.mailConfigService.Check(int64(uid))
+ response.Result(errCode, nil, context)
+}
\ No newline at end of file
diff --git a/server/api/customer.go b/server/api/customer.go
index 68d13fa..e01cbba 100644
--- a/server/api/customer.go
+++ b/server/api/customer.go
@@ -4,6 +4,7 @@ import (
"crm/models"
"crm/response"
"crm/service"
+ "log"
"strconv"
"github.com/gin-gonic/gin"
@@ -45,6 +46,21 @@ func (c *CustomerApi) Update(context *gin.Context) {
response.Result(errCode, nil, context)
}
+// 发送邮件给客户
+func (c *CustomerApi) SendMail(context *gin.Context) {
+ var param models.CustomerSendMailParam
+ uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
+ err := context.ShouldBind(¶m)
+ if uid <= 0 || err != nil {
+ response.Result(response.ErrCodeParamInvalid, nil, context)
+ log.Println(err)
+ return
+ }
+ param.Uid = int64(uid)
+ errCode := c.customerService.SendMail(¶m)
+ response.Result(errCode, nil, context)
+}
+
// 删除客户
func (c *CustomerApi) Delete(context *gin.Context) {
var param models.CustomerDeleteParam
diff --git a/server/common/mail.go b/server/common/mail.go
index 5ad1133..5ad9e00 100644
--- a/server/common/mail.go
+++ b/server/common/mail.go
@@ -2,13 +2,15 @@ package common
import (
"crm/global"
+ "crm/models"
"crypto/tls"
"fmt"
+ "log"
"gopkg.in/gomail.v2"
)
-// 发送邮件
+// 发送邮件(系统级别)
// QQ邮箱:SMTP 服务器地址:smtp.qq.com(SSL协议端口:465/994 | 非SSL协议端口:25)
// 163邮箱:SMTP 服务器地址:smtp.163.com(端口:25)
func SendMail(email, content string) error {
@@ -30,4 +32,28 @@ func SendMail(email, content string) error {
return err
}
return nil
+}
+
+// 发送邮件给客户(客户级别)
+func SendMailToCustomer(mp models.MailParam) error {
+ m := gomail.NewMessage()
+ m.SetHeader("From", mp.Sender)
+ m.SetHeader("To", mp.Receiver)
+ m.SetHeader("Subject", mp.Subject)
+ m.SetBody("text/html", mp.Content)
+ d := gomail.NewDialer(mp.Smtp, mp.Port, mp.Sender, mp.AuthCode)
+ d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
+ if err := d.DialAndSend(m); err != nil {
+ log.Printf("send mail to customer error : %s", err)
+ return err
+ }
+ return nil
+}
+
+// 检测STMP服务是否可连接
+func DialMail(smtp string, port int, sender, authCode string) error {
+ d := gomail.NewDialer(smtp, port, sender, authCode)
+ d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
+ _, err := d.Dial()
+ return err
}
\ No newline at end of file
diff --git a/server/dao/config.go b/server/dao/config.go
new file mode 100644
index 0000000..f900f10
--- /dev/null
+++ b/server/dao/config.go
@@ -0,0 +1,82 @@
+package dao
+
+import (
+ "crm/global"
+ "crm/models"
+ "time"
+)
+
+type MailConfigDao struct {
+}
+
+func NewMailConfigDao() *MailConfigDao {
+ return &MailConfigDao{}
+}
+
+func (m *MailConfigDao) Save(param *models.MailConfigSaveParam) error {
+ if !isExists(param.Creator) {
+ return create(param)
+ }
+ return update(param)
+}
+
+func (m *MailConfigDao) GetInfo(uid int64) (*models.MailConfig, error) {
+ var mc models.MailConfig
+ err := global.Db.Table(MAIL_CONFIG).Where("creator = ?", uid).First(&mc).Error
+ if err != nil {
+ return nil, err
+ }
+ return &mc, nil
+}
+
+func (m *MailConfigDao) UpdateStatus(param *models.MailConfigStatusParam) error {
+ mailConfig := models.MailConfig{
+ Status: param.Status,
+ Updated: time.Now().Unix(),
+ }
+ db := global.Db.Model(&mailConfig).Where("id = ? and creator = ?", param.Id, param.Creator)
+ if err := db.Updates(&mailConfig).Error; err != nil {
+ return err
+ }
+ return nil
+}
+
+func isExists(uid int64) bool {
+ var mc models.MailConfig
+ db := global.Db.Table(MAIL_CONFIG).Where("creator = ?", uid).First(&mc)
+ return db.RowsAffected != NumberNull
+}
+
+func create(param *models.MailConfigSaveParam) error {
+ mailConfig := models.MailConfig{
+ Stmp: param.Stmp,
+ Port: param.Port,
+ AuthCode: param.AuthCode,
+ Email: param.Email,
+ Status: 1,
+ Creator: param.Creator,
+ Created: time.Now().Unix(),
+ }
+ if err := global.Db.Create(&mailConfig).Error; err != nil {
+ return err
+ }
+ return nil
+}
+
+func update(param *models.MailConfigSaveParam) error {
+ mailConfig := models.MailConfig{
+ Id: param.Id,
+ Stmp: param.Stmp,
+ Port: param.Port,
+ AuthCode: param.AuthCode,
+ Email: param.Email,
+ Status: param.Status,
+ Creator: param.Creator,
+ Updated: time.Now().Unix(),
+ }
+ db := global.Db.Model(&mailConfig).Select("*").Omit("id", "creator", "created")
+ if err := db.Updates(&mailConfig).Error; err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/server/initialize/router.go b/server/initialize/router.go
index bda8bf9..d3c4d2b 100644
--- a/server/initialize/router.go
+++ b/server/initialize/router.go
@@ -39,6 +39,7 @@ func Router() {
route.GET("/customer/option", api.NewCustomerApi().QueryOption)
route.GET("/customer/export", api.NewCustomerApi().Export)
route.POST("/customer/create", api.NewCustomerApi().Create)
+ route.POST("/customer/send", api.NewCustomerApi().SendMail)
route.PUT("/customer/update", api.NewCustomerApi().Update)
route.DELETE("/customer/delete", api.NewCustomerApi().Delete)
@@ -62,6 +63,12 @@ func Router() {
// 仪表盘模块
route.GET("/dashboard/sum", api.NewDashboardApi().Summary)
+ // 配置模块
+ route.GET("/config/info", api.NewMailConfigApi().GetInfo)
+ route.GET("/config/check", api.NewMailConfigApi().Check)
+ route.PUT("/config/save", api.NewMailConfigApi().Save)
+ route.PUT("/config/status", api.NewMailConfigApi().UpdateStatus)
+
// 订阅模块
route.GET("/subscribe/info", api.NewSubscribeApi().GetInfo)
route.POST("/subscribe/pay", api.NewSubscribeApi().Pay)
diff --git a/server/models/common.go b/server/models/common.go
index 4542afa..e8cdcb5 100644
--- a/server/models/common.go
+++ b/server/models/common.go
@@ -5,3 +5,14 @@ type Page struct {
PageNum int `form:"pageNum" json:"pageNum"`
PageSize int `form:"pageSize" json:"pageSize"`
}
+
+// 发送邮件参数模型
+type MailParam struct {
+ Smtp string
+ Port int
+ AuthCode string
+ Sender string
+ Subject string
+ Content string
+ Receiver string
+}
diff --git a/server/models/config.go b/server/models/config.go
new file mode 100644
index 0000000..01fbd39
--- /dev/null
+++ b/server/models/config.go
@@ -0,0 +1,39 @@
+package models
+
+type MailConfig struct {
+ Id int64 `gorm:"primaryKey"`
+ Stmp string `gorm:"stmp"`
+ Port int `gorm:"port"`
+ AuthCode string `gorm:"auth_code"`
+ Email string `gorm:"email"`
+ Status int `gorm:"status"`
+ Creator int64 `gorm:"creator"`
+ Created int64 `gorm:"created"`
+ Updated int64 `gorm:"updated"`
+}
+
+type MailConfigSaveParam struct {
+ Id int64 `json:"id" binding:"omitempty,gt=0"`
+ Stmp string `json:"stmp" binding:"omitempty,ip|hostname"`
+ Port int `json:"port" binding:"omitempty,gt=0"`
+ AuthCode string `json:"authCode" binding:"omitempty,gt=0"`
+ Email string `json:"email" binding:"omitempty,email"`
+ Status int `json:"status" binding:"omitempty,oneof=1 2"`
+ Creator int64 `json:"creator" binding:"omitempty"`
+}
+
+type MailConfigStatusParam struct {
+ Id int64 `json:"id" binding:"required,gt=0"`
+ Status int `json:"status" binding:"required,oneof=1 2"`
+ Creator int64 `json:"creator" binding:"omitempty"`
+}
+
+type MailConfigInfo struct {
+ Id int64 `json:"id"`
+ Stmp string `json:"stmp"`
+ Port int `json:"port"`
+ AuthCode string `json:"authCode"`
+ Email string `json:"email"`
+ Status int `json:"status"`
+ Usability int `json:"usability"`
+}
diff --git a/server/models/customer.go b/server/models/customer.go
index 260b080..831b069 100644
--- a/server/models/customer.go
+++ b/server/models/customer.go
@@ -45,6 +45,13 @@ type CustomerUpdateParam struct {
Status int `json:"status" binding:"-"`
}
+type CustomerSendMailParam struct {
+ Uid int64 `json:"uid" binding:"-"`
+ Receiver string `json:"receiver" binding:"required,email"`
+ Subject string `json:"subject" binding:"omitempty,gt=0"`
+ Content string `json:"content" binding:"required,gt=0"`
+}
+
type CustomerDeleteParam struct {
Ids []int64 `json:"ids" binding:"required"`
}
diff --git a/server/response/errcode.go b/server/response/errcode.go
index 47174c0..57bf5fe 100644
--- a/server/response/errcode.go
+++ b/server/response/errcode.go
@@ -23,6 +23,10 @@ const (
ErrCodePayFailed = 20001 // 支付宝支付失败
ErrCodeFileExportFailed = 30001 // 文件导出失败
+
+ ErrCodeMailConfigInvalid = 50001 // 邮件服务配置无效
+ ErrCodeMailSendFailed = 50002 // 邮件发送失败
+ ErrCodeMailSendUnEnable = 50003 // 邮件服务未开启
)
var msg = map[int]string{
@@ -42,4 +46,7 @@ var msg = map[int]string{
ErrCodeEmailFormatInvalid: "email format invalid",
ErrCodeUserPassResetFailed: "user password reset failed",
ErrCodeFileExportFailed: "file export failed",
+ ErrCodeMailConfigInvalid: "mail config invalid",
+ ErrCodeMailSendFailed: "mail send failed",
+ ErrCodeMailSendUnEnable: "mail send server unEnable",
}
diff --git a/server/service/config.go b/server/service/config.go
new file mode 100644
index 0000000..f26c3ed
--- /dev/null
+++ b/server/service/config.go
@@ -0,0 +1,63 @@
+package service
+
+import (
+ "crm/common"
+ "crm/dao"
+ "crm/models"
+ "crm/response"
+)
+
+type MailConfigService struct {
+ mailConfigDao *dao.MailConfigDao
+}
+
+func NewMailConfigService() *MailConfigService {
+ return &MailConfigService{
+ mailConfigDao: dao.NewMailConfigDao(),
+ }
+}
+
+// 保存邮件服务配置
+func (m *MailConfigService) Save(param *models.MailConfigSaveParam) int {
+ if err := m.mailConfigDao.Save(param); err != nil {
+ return response.ErrCodeFailed
+ }
+ return response.ErrCodeSuccess
+}
+
+// 开启或关闭邮件服务
+func (m *MailConfigService) UpdateStatus(param *models.MailConfigStatusParam) int {
+ if err := m.mailConfigDao.UpdateStatus(param); err != nil {
+ return response.ErrCodeFailed
+ }
+ return response.ErrCodeSuccess
+}
+
+// 获取邮件配置信息
+func (m *MailConfigService) GetInfo(uid int64) (*models.MailConfigInfo, int) {
+ mc, err := m.mailConfigDao.GetInfo(uid)
+ if err != nil {
+ return nil, response.ErrCodeFailed
+ }
+ mailConfig := models.MailConfigInfo{
+ Id: mc.Id,
+ Stmp: mc.Stmp,
+ Port: mc.Port,
+ AuthCode: mc.AuthCode,
+ Email: mc.Email,
+ Status: mc.Status,
+ }
+ return &mailConfig, response.ErrCodeSuccess
+}
+
+// 检查邮件配置的有效性
+func (m *MailConfigService) Check(uid int64) int {
+ mc, err := m.mailConfigDao.GetInfo(uid)
+ if err != nil {
+ return response.ErrCodeMailConfigInvalid
+ }
+ if err = common.DialMail(mc.Stmp, mc.Port, mc.Email, mc.AuthCode); err != nil {
+ return response.ErrCodeMailConfigInvalid
+ }
+ return response.ErrCodeSuccess
+}
diff --git a/server/service/customer.go b/server/service/customer.go
index 23ad506..e42d79b 100644
--- a/server/service/customer.go
+++ b/server/service/customer.go
@@ -9,6 +9,11 @@ import (
"time"
)
+const (
+ Open = 1
+ Close = 2
+)
+
type CustomerService struct {
}
@@ -57,6 +62,31 @@ func (c *CustomerService) Update(param *models.CustomerUpdateParam) int {
return response.ErrCodeSuccess
}
+// 发送邮件给客户
+func (c *CustomerService) SendMail(param *models.CustomerSendMailParam) int {
+ var mc models.MailConfig
+ err := global.Db.Model(&models.MailConfig{}).Where("creator = ?", param.Uid).First(&mc).Error
+ if err != nil {
+ return response.ErrCodeMailSendFailed
+ }
+ if mc.Status == Close {
+ return response.ErrCodeMailSendUnEnable
+ }
+ mailParam := models.MailParam{
+ Smtp: mc.Stmp,
+ Port: mc.Port,
+ AuthCode: mc.AuthCode,
+ Sender: mc.Email,
+ Subject: param.Subject,
+ Content: param.Content,
+ Receiver: param.Receiver,
+ }
+ if err := common.SendMailToCustomer(mailParam); err != nil {
+ return response.ErrCodeMailSendFailed
+ }
+ return response.ErrCodeSuccess
+}
+
// 删除客户
func (c *CustomerService) Delete(param *models.CustomerDeleteParam) int {
if err := global.Db.Delete(&models.Customer{}, param.Ids).Error; err != nil {
diff --git a/web/src/api/config.js b/web/src/api/config.js
new file mode 100644
index 0000000..6701e70
--- /dev/null
+++ b/web/src/api/config.js
@@ -0,0 +1,37 @@
+import request from '../axios/index'
+
+// 保存邮件服务配置
+export function saveMailConfig(param) {
+ return request({
+ url: '/config/save',
+ method: 'put',
+ data: param,
+ })
+}
+
+// 开启或关闭邮件服务
+export function updateMailConfigStmpStatus(param) {
+ return request({
+ url: '/config/status',
+ method: 'put',
+ data: param,
+ })
+}
+
+// 获取邮件服务配置信息
+export function getMailConfig(param) {
+ return request({
+ url: '/config/info',
+ method: 'get',
+ data: param,
+ })
+}
+
+// 检查邮件配置的有效性
+export function checkMailConfig(param) {
+ return request({
+ url: '/config/check',
+ method: 'get',
+ data: param,
+ })
+}
\ No newline at end of file
diff --git a/web/src/api/customer.js b/web/src/api/customer.js
index 5bfad00..e01e43c 100644
--- a/web/src/api/customer.js
+++ b/web/src/api/customer.js
@@ -18,6 +18,15 @@ export function updateCustomer(param) {
})
}
+// 发送邮件给客户
+export function sendMailToCustomer(param) {
+ return request({
+ url: '/customer/send',
+ method: 'post',
+ data: param,
+ })
+}
+
// 删除客户
export function deleteCustomer(param) {
return request({
diff --git a/web/src/router/index.js b/web/src/router/index.js
index 86e2009..ac7826e 100644
--- a/web/src/router/index.js
+++ b/web/src/router/index.js
@@ -11,6 +11,7 @@ import Dashboard from '../views/Dashboard.vue'
import Customer from '../views/Customer.vue'
import Contract from '../views/Contract.vue'
import Product from '../views/Product.vue'
+import Config from '../views/Config.vue'
import Subscribe from '../views/Subscribe.vue'
import Result from '../views/Result.vue'
@@ -59,6 +60,11 @@ const routes = [
name: 'product',
component: Product,
},
+ {
+ path: '/config',
+ name: 'config',
+ component: Config,
+ },
{
path: '/subscribe',
name: 'subscribe',
diff --git a/web/src/views/Config.vue b/web/src/views/Config.vue
new file mode 100644
index 0000000..b5bf636
--- /dev/null
+++ b/web/src/views/Config.vue
@@ -0,0 +1,217 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存
+ 重置
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/views/Customer.vue b/web/src/views/Customer.vue
index 3a193b3..c75fed4 100644
--- a/web/src/views/Customer.vue
+++ b/web/src/views/Customer.vue
@@ -1,5 +1,5 @@
-
+
@@ -27,6 +27,11 @@
{{ text }}
+
+
+
+
+
已成交
未成交
@@ -121,21 +126,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+