初始化项目

This commit is contained in:
2025-12-05 14:40:33 +08:00
parent ee44c7ec1f
commit 78d549fa4c
11 changed files with 427 additions and 0 deletions

57
Dockerfile Normal file
View File

@@ -0,0 +1,57 @@
FROM golang:1.25.3
RUN go env -w GO111MODULE=on
RUN go env -w GOPROXY=https://goproxy.cn,direct
ENV WORKDIR /usr/local/bin/app
WORKDIR $WORKDIR
ENV TIME_ZONE=Asia/Seoul
RUN useradd -ms /bin/bash golang
RUN ln -sf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime \
&& chmod 0755 /usr/bin/wall \
&& chmod 0755 /usr/bin/passwd \
&& chmod 0755 /usr/bin/newgrp \
&& chmod 0755 /usr/bin/chfn \
&& chmod 0755 /usr/bin/chage \
&& chmod 0755 /usr/bin/gpasswd \
&& chmod 0755 /usr/bin/chsh \
&& chmod 0755 /usr/bin/expiry \
&& chmod 0755 /bin/umount \
&& chmod 0755 /bin/mount \
&& chmod 0755 /bin/su \
&& chmod 0755 /sbin/unix_chkpwd \
&& chmod 110 /usr/bin/chfn \
&& chmod 110 /usr/bin/passwd \
&& chmod 110 /usr/bin/newgrp \
&& chmod 110 /usr/bin/chsh \
&& chmod 110 /usr/bin/wall \
&& chmod 110 /usr/bin/gpasswd \
&& chmod 110 /usr/bin/expiry \
&& chmod 110 /usr/bin/chage \
&& chmod 110 /bin/mount \
&& chmod 110 /bin/umount \
&& chmod 110 /bin/su \
&& chmod 110 /sbin/unix_chkpwd \
&& chmod 110 /usr/lib/openssh/ssh-keysign \
&& chmod 110 /usr/bin/ssh-agent
COPY go.mod go.sum ./
# RUN chown -R golang:golang $WORKDIR
RUN go mod download && go mod verify
COPY ../../cidService $WORKDIR
RUN chown -R golang:golang $WORKDIR
# Remove SetUID, SetGID
RUN chmod 0755 /usr/local/bin/app/api \
&& chmod 0755 /usr/local/bin/app/common \
&& chmod 0755 /usr/local/bin/app/middleware \
&& chmod 0755 /usr/local/bin/app/model \
&& rm -rf .git \
&& rm -rf .gitignore \
&& rm -rf .gitlab-ci.yml \
&& rm -rf Dockerfile \
&& rm -rf config-local.yml
USER golang
RUN go build -v -o /usr/local/bin/app ./...
EXPOSE 3002
CMD ./cidService

40
config.yml Normal file
View File

@@ -0,0 +1,40 @@
server:
address : ":3002"
name: "cidService"
jwt:
secret: "abcdefghijklmnopqrstuvwxyz"
rate:
limit: 200
burst: 300
mongo:
logger:
level: "all"
stdout: true
address: "mongodb://192.168.3.200:27017/cid_service?retryWrites=true"
redis:
# 集群模式配置方法
default:
address: 192.168.3.200:6379
db: 0
idleTimeout: "60s" #连接最大空闲时间使用时间字符串例如30s/1m/1d
maxConnLifetime: "90s" #连接最长存活时间使用时间字符串例如30s/1m/1d
waitTimeout: "60s" #等待连接池连接的超时时间使用时间字符串例如30s/1m/1d
dialTimeout: "30s" #TCP连接的超时时间使用时间字符串例如30s/1m/1d
readTimeout: "30s" #TCP的Read操作超时时间使用时间字符串例如30s/1m/1d
writeTimeout: "30s" #TCP的Write操作超时时间使用时间字符串例如30s/1m/1d
maxActive: 100
consul:
address: 192.168.3.200:8500
# pass: jiahui8888
rabbitMQ:
host: 192.168.3.200
port: 5672
username: guest # 默认用户名
password: guest # 默认密码
jaeger: #链路追踪
addr: 192.168.3.200:4318
# RAGFlow 智能客服配置
ragflow:
base_url: "http://192.168.3.200:9380" # RAGFlow 服务地址
api_key: "ragflow-your-api-key-here" # RAGFlow API Key登录 RAGFlow 管理界面 -> 设置 -> API Keys

1
consts/errors.go Normal file
View File

@@ -0,0 +1 @@
package consts

1
consts/redis_key.go Normal file
View File

@@ -0,0 +1 @@
package consts

View File

@@ -0,0 +1,27 @@
package controller
import (
"cidService/model/dto"
"cidService/service"
"context"
)
type cData struct{}
var Data = &cData{}
// Add 添加数据
func (c *cData) Add(ctx context.Context, req *dto.AddDataReq) (res *dto.AddDataRes, err error) {
return service.Data.Add(ctx, req)
}
// Update 更新数据
func (c *cData) Update(ctx context.Context, req *dto.UpdateDataReq) (res interface{}, err error) {
err = service.Data.Update(ctx, req)
return
}
// List 获取数据列表
func (c *cData) List(ctx context.Context, req *dto.ListDataReq) (res *dto.ListDataRes, err error) {
return service.Data.List(ctx, req)
}

121
dao/data_dao.go Normal file
View File

@@ -0,0 +1,121 @@
package dao
import (
"cidService/model/dto"
"cidService/model/entity"
"context"
"gitee.com/red-future---jilin-g/common/http"
"gitee.com/red-future---jilin-g/common/mongo"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
var Data = &data{}
type data struct{}
// Insert 插入数据
func (d *data) Insert(ctx context.Context, data *entity.Data) (err error) {
// 获取stream消息
redis := g.Redis()
streamMsg, err := redis.Do(ctx, "XREAD", "STREAMS", "data_stream", "$")
if err != nil {
g.Log().Errorf(ctx, "获取stream消息失败: %v", err)
} else {
g.Log().Infof(ctx, "获取到stream消息: %v", streamMsg)
}
_, err = mongo.Insert(ctx, []interface{}{data}, entity.DataCollection)
return
}
// Update 更新数据
func (d *data) Update(ctx context.Context, req *dto.UpdateDataReq) (err error) {
objectId, err := bson.ObjectIDFromHex(req.Id)
if err != nil {
return
}
filter := bson.M{"_id": objectId}
// 构建动态更新字段
updateFields := bson.M{}
if !g.IsEmpty(req.CustomerId) {
updateFields["customerId"] = req.CustomerId
}
if !g.IsEmpty(req.CustomerServiceId) {
updateFields["customerServiceId"] = req.CustomerServiceId
}
if req.IsInbound != nil {
updateFields["isInbound"] = *req.IsInbound
}
if req.IsActive != nil {
updateFields["isActive"] = *req.IsActive
}
if req.IsServed != nil {
updateFields["isServed"] = *req.IsServed
}
if req.HasSentContactCard != nil {
updateFields["hasSentContactCard"] = *req.HasSentContactCard
}
if req.HasSentNameCard != nil {
updateFields["hasSentNameCard"] = *req.HasSentNameCard
}
if req.HasLeftContactInfo != nil {
updateFields["hasLeftContactInfo"] = *req.HasLeftContactInfo
}
if len(updateFields) > 0 {
update := bson.M{"$set": updateFields}
_, err = mongo.Update(ctx, filter, update, entity.DataCollection)
}
return
}
// buildListFilter 构建列表查询的过滤条件
func (d *data) buildListFilter(req *dto.ListDataReq) bson.M {
filter := bson.M{}
if !g.IsEmpty(req.CustomerId) {
filter["customerId"] = req.CustomerId
}
if !g.IsEmpty(req.CustomerServiceId) {
filter["customerServiceId"] = req.CustomerServiceId
}
return filter
}
// checkTotalCount 检查总数
func (d *data) checkTotalCount(ctx context.Context, filter bson.M) (total int64, err error) {
total, err = mongo.Count(ctx, filter, entity.DataCollection)
return
}
// List 获取数据列表
func (d *data) List(ctx context.Context, req *dto.ListDataReq) (list []*entity.Data, total int64, err error) {
// 构建查询过滤条件
filter := d.buildListFilter(req)
// 检查总数
total, err = d.checkTotalCount(ctx, filter)
if err != nil {
return
}
// 分页参数处理
pageNum := req.PageNum
if pageNum <= 0 {
pageNum = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = http.PageSize
}
limit := int64(pageSize)
skip := int64((pageNum - 1) * pageSize)
opts := options.Find().SetLimit(limit).SetSkip(skip).SetSort(bson.M{"sessionStartTime": -1})
err = mongo.Find(ctx, filter, &list, entity.DataCollection, opts)
return
}

14
go.mod Normal file
View File

@@ -0,0 +1,14 @@
module cidService
go 1.25.3
require (
gitee.com/red-future---jilin-g/common v0.1.9
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.5
github.com/gogf/gf/v2 v2.9.5
go.mongodb.org/mongo-driver/v2 v2.4.0
golang.org/x/net v0.47.0
)
//replace gitee.com/red-future---jilin-g/common v0.1.9 => ../common

21
main.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"cidService/controller"
"gitee.com/red-future---jilin-g/common/http"
"gitee.com/red-future---jilin-g/common/jaeger"
_ "gitee.com/red-future---jilin-g/common/mongo"
_ "gitee.com/red-future---jilin-g/common/ragflow" // RAGFlow 客户端自动初始化
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
"golang.org/x/net/context"
)
func main() {
defer jaeger.ShutDown(context.Background())
http.RouteRegister([]interface{}{
controller.Data,
})
select {}
}

67
model/dto/data_dto.go Normal file
View File

@@ -0,0 +1,67 @@
package dto
import (
"cidService/model/entity"
"gitee.com/red-future---jilin-g/common/http"
"github.com/gogf/gf/v2/frame/g"
)
// AddDataReq 添加数据
type AddDataReq struct {
g.Meta `path:"/add" method:"post" tags:"数据管理" summary:"添加数据" dc:"记录客服与客户的交互数据"` // 路由: POST /data/add
CustomerId string `json:"customerId" v:"required"` // 客户ID
CustomerServiceId string `json:"customerServiceId" v:"required"` // 客服ID
CustomerServicePlatform string `json:"customerServicePlatform" v:"required"` // 客服平台
CustomerServiceName string `json:"customerServiceName" v:"required"` // 客服名称
IsInbound bool `json:"isInbound"` // 是否进线
IsActive bool `json:"isActive"` // 是否活跃
IsServed bool `json:"isServed"` // 是否接待
HasSentContactCard bool `json:"hasSentContactCard"` // 是否发联系卡
HasSentNameCard bool `json:"hasSentNameCard"` // 是否发名片
HasLeftContactInfo bool `json:"hasLeftContactInfo"` // 是否留资
SessionStartTime int64 `json:"sessionStartTime"` // 会话开始时间
MessageTime int64 `json:"messageTime"` // 消息时间
}
type AddDataRes struct {
Id string `json:"id"`
}
// UpdateDataReq 更新数据
type UpdateDataReq struct {
g.Meta `path:"/update" method:"post" tags:"数据管理" summary:"更新数据" dc:"更新客服交互数据"` // 路由: POST /data/update
Id string `json:"id" v:"required"` // ID
CustomerId string `json:"customerId"`
CustomerServiceId string `json:"customerServiceId"`
CustomerServicePlatform string `json:"customerServicePlatform"`
CustomerServiceName string `json:"customerServiceName"`
IsInbound *bool `json:"isInbound"` // 使用指针以区分 false 和未传值
IsActive *bool `json:"isActive"`
IsServed *bool `json:"isServed"`
HasSentContactCard *bool `json:"hasSentContactCard"`
HasSentNameCard *bool `json:"hasSentNameCard"`
HasLeftContactInfo *bool `json:"hasLeftContactInfo"`
SessionStartTime int64 `json:"sessionStartTime"`
MessageTime int64 `json:"messageTime"`
}
// GetDataReq 获取单个数据
type GetDataReq struct {
g.Meta `path:"/one" method:"get" tags:"数据管理" summary:"获取数据详情" dc:"根据ID获取单条数据记录"` // 路由: GET /data/one
Id string `json:"id" v:"required"` // ID
}
// ListDataReq 获取数据列表
type ListDataReq struct {
g.Meta `path:"/list" method:"get" tags:"数据管理" summary:"获取数据列表" dc:"分页查询交互数据,支持按客户、客服、时间筛选"` // 路由: GET /data/list
http.Page
CustomerId string `json:"customerId"` // 筛选客户ID
CustomerServiceId string `json:"customerServiceId"` // 筛选客服ID
DateRange []string `json:"dateRange"` // 筛选:时间范围 [start, end]
}
type ListDataRes struct {
List []*entity.Data `json:"list"`
Total int `json:"total"`
}

25
model/entity/data.go Normal file
View File

@@ -0,0 +1,25 @@
package entity
import (
"gitee.com/red-future---jilin-g/common/do"
)
const DataCollection = "data"
type Data struct {
do.MongoBaseDO `bson:",inline"` // 嵌入基础字段Id, Creator, CreatedAt, Updater, UpdatedAt, TenantId, IsDeleted
// 业务字段
CustomerId string `bson:"customerId" json:"customerId"` // 客户ID
CustomerServiceId string `bson:"customerServiceId" json:"customerServiceId"` // 客服ID
CustomerServicePlatform string `bson:"customerServicePlatform" json:"customerServicePlatform"` // 客服平台
CustomerServiceName string `bson:"customerServiceName" json:"customerServiceName"` // 客服名称
IsInbound bool `bson:"isInbound" json:"isInbound"` // 用户是否点开了客服页面
IsActive bool `bson:"isActive" json:"isActive"` // 用户是否开口询问
IsServed bool `bson:"isServed" json:"isServed"` // 客服是否回答了用户
HasSentContactCard bool `bson:"hasSentContactCard" json:"hasSentContactCard"` // 客服是否发送了联系卡
HasSentNameCard bool `bson:"hasSentNameCard" json:"hasSentNameCard"` // 客服是否发送了名称卡
HasLeftContactInfo bool `bson:"hasLeftContactInfo" json:"hasLeftContactInfo"` // 用户是否留下了联系信息
SessionStartTime int64 `bson:"sessionStartTime" json:"sessionStartTime"` // 业务数据的时间
MessageTime int64 `bson:"messageTime" json:"messageTime"` // 消息时间
}

53
service/data_service.go Normal file
View File

@@ -0,0 +1,53 @@
package service
import (
"cidService/dao"
"cidService/model/dto"
"cidService/model/entity"
"context"
"time"
"github.com/gogf/gf/v2/util/gconv"
)
var Data = new(data)
type data struct{}
// Add 添加数据
func (s *data) Add(ctx context.Context, req *dto.AddDataReq) (res *dto.AddDataRes, err error) {
data := &entity.Data{}
if err = gconv.Struct(req, data); err != nil {
return
}
// 设置基础字段
now := time.Now()
data.CreatedAt = now
data.UpdatedAt = now
data.IsDeleted = false
// 注意Creator、Updater、TenantId 保持零值,不设置
if err = dao.Data.Insert(ctx, data); err != nil {
return
}
res = &dto.AddDataRes{Id: data.Id.Hex()}
return
}
// Update 更新数据
func (s *data) Update(ctx context.Context, req *dto.UpdateDataReq) (err error) {
return dao.Data.Update(ctx, req)
}
// List 获取数据列表
func (s *data) List(ctx context.Context, req *dto.ListDataReq) (res *dto.ListDataRes, err error) {
list, total, err := dao.Data.List(ctx, req)
if err != nil {
return
}
res = &dto.ListDataRes{
List: list,
Total: int(total),
}
return
}