471 lines
12 KiB
Go
471 lines
12 KiB
Go
|
package models
|
|||
|
|
|||
|
import (
|
|||
|
"errors"
|
|||
|
"fmt"
|
|||
|
"github.com/go-admin-team/go-admin-core/logger"
|
|||
|
"github.com/xuri/excelize/v2"
|
|||
|
"gorm.io/gorm"
|
|||
|
"math/rand"
|
|||
|
"strings"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
const (
|
|||
|
QueryTimeFormat = "2006-01-02T15:04:05+08:00"
|
|||
|
TimeFormat = "2006-01-02 15-04-05"
|
|||
|
ExportUrl = "https://admin.go2switch.cn/load/export/"
|
|||
|
ExportExcelFlag = 1
|
|||
|
ExportPath = "/www/server/images/export/"
|
|||
|
)
|
|||
|
|
|||
|
var certMap = map[int]string{
|
|||
|
1: "电工证",
|
|||
|
2: "焊工证",
|
|||
|
3: "叉车证",
|
|||
|
4: "制冷与空调作业证",
|
|||
|
5: "高空作业证",
|
|||
|
6: "危化品作业证",
|
|||
|
7: "安全员证",
|
|||
|
8: "其他工种上岗证",
|
|||
|
}
|
|||
|
|
|||
|
var WhiteList = map[string]int{
|
|||
|
"15019230751": 1,
|
|||
|
"18025373706": 2,
|
|||
|
}
|
|||
|
|
|||
|
type SendDataReq struct {
|
|||
|
Url string `json:"url"`
|
|||
|
Uid string `json:"uid"`
|
|||
|
}
|
|||
|
|
|||
|
type OppoSendDataReq struct {
|
|||
|
PageId int64 `json:"pageId"`
|
|||
|
OwnerId int64 `json:"ownerId"`
|
|||
|
Ip string `json:"ip"`
|
|||
|
Tid string `json:"tid"`
|
|||
|
Lbid string `json:"lbid"`
|
|||
|
Items []ClueDataItem `json:"items"`
|
|||
|
TransformType int `json:"transformType"`
|
|||
|
PageType int `json:"pageType,omitempty"`
|
|||
|
PayId string `json:"payId,omitempty"`
|
|||
|
PayAmount string `json:"payAmount,omitempty"`
|
|||
|
RefundId string `json:"refundId,omitempty"`
|
|||
|
Amount string `json:"amount,omitempty"`
|
|||
|
CustomId string `json:"customId,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type ClueDataItem struct {
|
|||
|
Column string `json:"column"`
|
|||
|
Type string `json:"type"`
|
|||
|
IfNeed bool `json:"ifNeed"`
|
|||
|
Desc string `json:"desc"`
|
|||
|
Value string `json:"value"`
|
|||
|
Options []string `json:"options,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type OppoSendDataResp struct {
|
|||
|
Code int `json:"code"`
|
|||
|
Msg string `json:"msg"`
|
|||
|
}
|
|||
|
|
|||
|
type NewSendDataReq struct {
|
|||
|
Url string `json:"url" binding:"required"` // 页面url路径
|
|||
|
Name string `json:"name" binding:"required"` // 姓名
|
|||
|
Tel string `json:"tel" binding:"required"` // 电话
|
|||
|
Type int `json:"type" binding:"required"` // 广告页类型 1-八大员;2-旅游
|
|||
|
CertList []int `json:"cert_list"` // 证书列表:1-电工证,2-焊工证,3-叉车证,4-制冷与空调作业证,5-高空作业证,6-危化品作业证,7-安全员证,8-其他工种上岗证
|
|||
|
Data interface{} `json:"data"` // 其他选择内容
|
|||
|
}
|
|||
|
|
|||
|
// TravelData 旅游广告页其他参数
|
|||
|
type TravelData struct {
|
|||
|
TravelCount int `json:"travel_count"` // 出行人数
|
|||
|
}
|
|||
|
|
|||
|
type ClueUser struct {
|
|||
|
Model
|
|||
|
Name string `json:"name"` // 姓名
|
|||
|
Tel string `json:"tel" gorm:"index"` // 电话
|
|||
|
Cert string `json:"cert" gorm:"index"` // 证书列表
|
|||
|
}
|
|||
|
|
|||
|
type ClueUserTravel struct {
|
|||
|
Model
|
|||
|
Name string `json:"name"` // 姓名
|
|||
|
Tel string `json:"tel" gorm:"index"` // 电话
|
|||
|
}
|
|||
|
|
|||
|
type GetUserDataReq struct {
|
|||
|
Name string `json:"name"` // 姓名
|
|||
|
Tel string `json:"tel"` // 电话
|
|||
|
StartTime string `json:"start_time"` // 开始时间
|
|||
|
EndTime string `json:"end_time"` // 结束时间
|
|||
|
Type int `json:"type"` // 广告页类型 1-八大员;2-旅游
|
|||
|
IsExport uint32 `json:"is_export"` // 1-导出
|
|||
|
PageIndex int `json:"pageIndex"` // 页码
|
|||
|
PageSize int `json:"pageSize"` // 页面条数
|
|||
|
}
|
|||
|
|
|||
|
type GetUserDataResp struct {
|
|||
|
Total int64 `json:"total"` // 总条数
|
|||
|
PageIndex int `json:"pageIndex"` // 页码
|
|||
|
PageSize int `json:"pageSize"` // 页面条数
|
|||
|
ExportUrl string `json:"export_url"` // 导出excel地址
|
|||
|
List []ClueUser `json:"list"`
|
|||
|
}
|
|||
|
|
|||
|
type ClueCode struct {
|
|||
|
Model
|
|||
|
Phone string `json:"phone" gorm:"index"` // 电话
|
|||
|
Code int `json:"code"` // 验证码
|
|||
|
Expiry time.Time `json:"expiry"` // 过期时间
|
|||
|
}
|
|||
|
|
|||
|
type GetCodeReq struct {
|
|||
|
Tel string `json:"tel" binding:"required"` // 电话
|
|||
|
Type int `json:"type" binding:"required"` // 广告页类型 1-八大员;2-旅游
|
|||
|
}
|
|||
|
|
|||
|
type GetCodeResp struct {
|
|||
|
Code int `json:"code"` // 验证码
|
|||
|
}
|
|||
|
|
|||
|
type VerifyCodeReq struct {
|
|||
|
Tel string `json:"tel" binding:"required"` // 电话
|
|||
|
Code int `json:"code" binding:"required"` // 验证码
|
|||
|
}
|
|||
|
|
|||
|
type GetPhoneWithTokenReq struct {
|
|||
|
SpToken string `json:"SpToken" binding:"required"` // JSSDK 获取的号码认证 Token
|
|||
|
}
|
|||
|
|
|||
|
type RecordLog struct {
|
|||
|
Data interface{} `json:"data"`
|
|||
|
}
|
|||
|
|
|||
|
func GetUserDataList(req *GetUserDataReq, Db *gorm.DB) (*GetUserDataResp, error) {
|
|||
|
page := req.PageIndex - 1
|
|||
|
if page < 0 {
|
|||
|
page = 0
|
|||
|
}
|
|||
|
if req.PageSize == 0 {
|
|||
|
req.PageSize = 10
|
|||
|
}
|
|||
|
|
|||
|
resp := &GetUserDataResp{
|
|||
|
PageIndex: req.PageIndex,
|
|||
|
PageSize: req.PageSize,
|
|||
|
}
|
|||
|
|
|||
|
var qs *gorm.DB
|
|||
|
if req.Type == 2 { // 旅游
|
|||
|
qs = Db.Table("clue_user_travel")
|
|||
|
} else {
|
|||
|
qs = Db.Table("clue_user")
|
|||
|
}
|
|||
|
|
|||
|
if req.Name != "" {
|
|||
|
qs = qs.Where("name=?", req.Name)
|
|||
|
}
|
|||
|
if req.Tel != "" {
|
|||
|
qs = qs.Where("tel=?", req.Tel)
|
|||
|
}
|
|||
|
if req.StartTime != "" {
|
|||
|
startTime := req.StartTime
|
|||
|
loc, _ := time.LoadLocation("Asia/Shanghai") // 以东八区为例
|
|||
|
start, err := time.ParseInLocation(QueryTimeFormat, startTime, loc)
|
|||
|
if err != nil {
|
|||
|
logger.Errorf("Error parsing start time: %v", err)
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
qs = qs.Where("created_at >= ?", start)
|
|||
|
}
|
|||
|
if req.EndTime != "" {
|
|||
|
endTime := req.EndTime
|
|||
|
loc, _ := time.LoadLocation("Asia/Shanghai") // 以东八区为例
|
|||
|
end, err := time.ParseInLocation(QueryTimeFormat, endTime, loc)
|
|||
|
if err != nil {
|
|||
|
logger.Errorf("Error parsing end time: %v", err)
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
qs = qs.Where("created_at <= ?", end)
|
|||
|
}
|
|||
|
fmt.Printf("Start Time: %v, End Time: %v", req.StartTime, req.EndTime)
|
|||
|
|
|||
|
var count int64
|
|||
|
err := qs.Count(&count).Error
|
|||
|
if err != nil {
|
|||
|
logger.Error("count err:", err)
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
var orders []ClueUser
|
|||
|
if req.IsExport == ExportExcelFlag { // 导出excel
|
|||
|
err := qs.Order("id DESC").Find(&orders).Error
|
|||
|
if err != nil && err != RecordNotFound {
|
|||
|
logger.Error("erp commodity list err:", err)
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
var filePath string
|
|||
|
if req.Type == 2 { // 旅游
|
|||
|
filePath, err = travelUserDataExport(orders)
|
|||
|
} else {
|
|||
|
filePath, err = userDataExport(orders)
|
|||
|
}
|
|||
|
if err != nil {
|
|||
|
return nil, nil
|
|||
|
}
|
|||
|
resp.ExportUrl = filePath
|
|||
|
} else {
|
|||
|
err := qs.Order("id DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&orders).Error
|
|||
|
if err != nil && err != RecordNotFound {
|
|||
|
logger.Error("erp commodity list err:", err)
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
resp.List = orders
|
|||
|
resp.Total = count
|
|||
|
}
|
|||
|
|
|||
|
return resp, nil
|
|||
|
}
|
|||
|
|
|||
|
// userDataExport 导出用户数据-八大员
|
|||
|
func userDataExport(req []ClueUser) (string, error) {
|
|||
|
file := excelize.NewFile()
|
|||
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
|||
|
if err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
|
|||
|
url := ExportUrl
|
|||
|
//url := "/Users/max/Documents/"
|
|||
|
fileName := time.Now().Format(TimeFormat) + "-八大员操作证-用户数据" + ".xlsx"
|
|||
|
fmt.Println("url fileName:", url+fileName)
|
|||
|
|
|||
|
title := []interface{}{"姓名", "电话", "工种操作证", "提交时间"}
|
|||
|
cell, _ := excelize.CoordinatesToCellName(1, 1)
|
|||
|
if err = streamWriter.SetRow(cell, title); err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
|
|||
|
var row []interface{}
|
|||
|
nExcelStartRow := 0
|
|||
|
for rowId := 0; rowId < len(req); rowId++ {
|
|||
|
row = []interface{}{
|
|||
|
req[rowId].Name,
|
|||
|
req[rowId].Tel,
|
|||
|
req[rowId].Cert,
|
|||
|
req[rowId].CreatedAt,
|
|||
|
}
|
|||
|
|
|||
|
cell, _ = excelize.CoordinatesToCellName(1, nExcelStartRow+2)
|
|||
|
if err = streamWriter.SetRow(cell, row); err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
nExcelStartRow++
|
|||
|
|
|||
|
}
|
|||
|
if err := streamWriter.Flush(); err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
fmt.Println("save fileName:", url+fileName)
|
|||
|
if err := file.SaveAs(ExportPath + fileName); err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
return url + fileName, nil
|
|||
|
}
|
|||
|
|
|||
|
// userDataExport 导出用户数据-旅游
|
|||
|
func travelUserDataExport(req []ClueUser) (string, error) {
|
|||
|
file := excelize.NewFile()
|
|||
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
|||
|
if err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
|
|||
|
url := ExportUrl
|
|||
|
//url := "/Users/max/Documents/"
|
|||
|
fileName := time.Now().Format(TimeFormat) + "-重庆旅游-用户数据" + ".xlsx"
|
|||
|
fmt.Println("url fileName:", url+fileName)
|
|||
|
|
|||
|
title := []interface{}{"姓名", "电话", "提交时间"}
|
|||
|
cell, _ := excelize.CoordinatesToCellName(1, 1)
|
|||
|
if err = streamWriter.SetRow(cell, title); err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
|
|||
|
var row []interface{}
|
|||
|
nExcelStartRow := 0
|
|||
|
for rowId := 0; rowId < len(req); rowId++ {
|
|||
|
row = []interface{}{
|
|||
|
req[rowId].Name,
|
|||
|
req[rowId].Tel,
|
|||
|
req[rowId].CreatedAt,
|
|||
|
}
|
|||
|
|
|||
|
cell, _ = excelize.CoordinatesToCellName(1, nExcelStartRow+2)
|
|||
|
if err = streamWriter.SetRow(cell, row); err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
nExcelStartRow++
|
|||
|
|
|||
|
}
|
|||
|
if err := streamWriter.Flush(); err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
fmt.Println("save fileName:", url+fileName)
|
|||
|
if err := file.SaveAs(ExportPath + fileName); err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
}
|
|||
|
return url + fileName, nil
|
|||
|
}
|
|||
|
|
|||
|
// ConvertCertListToString 转换证书列表数据
|
|||
|
func ConvertCertListToString(certList []int) string {
|
|||
|
var result []string
|
|||
|
for _, cert := range certList {
|
|||
|
if certName, ok := certMap[cert]; ok {
|
|||
|
result = append(result, certName)
|
|||
|
}
|
|||
|
}
|
|||
|
return strings.Join(result, ",")
|
|||
|
}
|
|||
|
|
|||
|
func GetSMSCode(req *GetCodeReq, db *gorm.DB, nType int) (*GetCodeResp, error) {
|
|||
|
// Generate random 6-digit OTP
|
|||
|
rand.Seed(time.Now().UnixNano())
|
|||
|
otp := rand.Intn(900000) + 100000
|
|||
|
expiry := time.Now().Add(5 * time.Minute)
|
|||
|
|
|||
|
// 发送短信
|
|||
|
var err error
|
|||
|
var message string
|
|||
|
if nType == 2 { // 重庆旅游
|
|||
|
message = fmt.Sprintf("【边城旅行】短信验证码:%d,有效期5分钟,您正在领取优惠名额。如非本人操作,请忽略本短信。", otp)
|
|||
|
} else { // 八大员操作证
|
|||
|
message = fmt.Sprintf("【捷速教育】短信验证码:%d,有效期5分钟,您正在领取免费报考福利。如非本人操作,请忽略本短信。", otp)
|
|||
|
}
|
|||
|
fmt.Println(message)
|
|||
|
var phoneList []string
|
|||
|
phoneList = append(phoneList, req.Tel)
|
|||
|
err = GtSendMessage(phoneList, message)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
// 成功后记录数据
|
|||
|
codeData := &ClueCode{}
|
|||
|
codeData.Phone = req.Tel
|
|||
|
codeData.Code = otp
|
|||
|
codeData.Expiry = expiry
|
|||
|
|
|||
|
err = db.Create(codeData).Error
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
return &GetCodeResp{
|
|||
|
Code: otp,
|
|||
|
}, nil
|
|||
|
}
|
|||
|
|
|||
|
func VerifySMSCode(req *VerifyCodeReq, db *gorm.DB) error {
|
|||
|
var codeDataList []ClueCode
|
|||
|
err := db.Model(&ClueCode{}).Where("phone = ?", req.Tel).Find(&codeDataList).Error
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
|
|||
|
if len(codeDataList) == 0 {
|
|||
|
return errors.New("未查询到验证码,请重新获取")
|
|||
|
}
|
|||
|
|
|||
|
// 校验当前验证码
|
|||
|
flag := false
|
|||
|
for _, v := range codeDataList {
|
|||
|
if req.Code != v.Code {
|
|||
|
continue
|
|||
|
} else {
|
|||
|
if time.Now().After(v.Expiry) {
|
|||
|
return errors.New("验证码已过期,请重新获取")
|
|||
|
} else {
|
|||
|
flag = true
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if !flag {
|
|||
|
return errors.New("校验失败,请重新获取")
|
|||
|
}
|
|||
|
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// CheckPhone 检查手机号是否存在
|
|||
|
func CheckPhone(nType int, tel string, db *gorm.DB) (bool, error) {
|
|||
|
var exist bool
|
|||
|
var whiteExist bool
|
|||
|
var err error
|
|||
|
if nType == 1 {
|
|||
|
exist, err = QueryRecordExist(fmt.Sprintf("SELECT * FROM clue_user WHERE tel='%s'", tel), db)
|
|||
|
if err != nil {
|
|||
|
logger.Errorf("CheckPhone err:", err)
|
|||
|
return false, err
|
|||
|
}
|
|||
|
} else if nType == 2 {
|
|||
|
exist, err = QueryRecordExist(fmt.Sprintf("SELECT * FROM clue_user_travel WHERE tel='%s'", tel), db)
|
|||
|
if err != nil {
|
|||
|
logger.Errorf("CheckPhone err:", err)
|
|||
|
return false, err
|
|||
|
}
|
|||
|
} else {
|
|||
|
return false, errors.New("参数type类型有误")
|
|||
|
}
|
|||
|
|
|||
|
whiteExist, err = QueryRecordExist(fmt.Sprintf("SELECT * FROM white_list WHERE phone='%s'", tel), db)
|
|||
|
if err != nil {
|
|||
|
logger.Errorf("CheckPhone err:", err)
|
|||
|
return false, err
|
|||
|
}
|
|||
|
|
|||
|
if exist && !whiteExist {
|
|||
|
return true, nil
|
|||
|
}
|
|||
|
|
|||
|
return false, nil
|
|||
|
}
|
|||
|
|
|||
|
// RecordUserData 回传成功后记录到数据库
|
|||
|
func RecordUserData(req *NewSendDataReq, db *gorm.DB) error {
|
|||
|
if req.Type == 1 {
|
|||
|
h5Data := &ClueUser{}
|
|||
|
h5Data.Name = req.Name
|
|||
|
h5Data.Tel = req.Tel
|
|||
|
if len(req.CertList) != 0 {
|
|||
|
h5Data.Cert = ConvertCertListToString(req.CertList)
|
|||
|
}
|
|||
|
|
|||
|
err := db.Create(h5Data).Error
|
|||
|
if err != nil {
|
|||
|
logger.Info("Create ClueUser err:", err)
|
|||
|
return err
|
|||
|
}
|
|||
|
} else if req.Type == 2 {
|
|||
|
h5Data := &ClueUserTravel{}
|
|||
|
h5Data.Name = req.Name
|
|||
|
h5Data.Tel = req.Tel
|
|||
|
|
|||
|
err := db.Create(h5Data).Error
|
|||
|
if err != nil {
|
|||
|
logger.Info("Create ClueUser err:", err)
|
|||
|
return err
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return nil
|
|||
|
}
|