diff --git a/.env b/.env new file mode 100644 index 0000000..cae7d7d --- /dev/null +++ b/.env @@ -0,0 +1,17 @@ +# Project Name +PROJECT_NAME=LingXi_CRM + +# Ports +WEB_PORT=11000 +SERVER_PORT=11001 +DB_PORT=11002 +REDIS_PORT=11003 + +# Database +DB_NAME=crm +DB_USER=root +DB_PASSWORD=lingxi_crm_pass +DB_ROOT_PASSWORD=lingxi_crm_root_pass + +# Backend config +API_BASE_URL=http://localhost:${SERVER_PORT}/api diff --git a/.work/工作流状态.md b/.work/工作流状态.md new file mode 100644 index 0000000..ca48b51 --- /dev/null +++ b/.work/工作流状态.md @@ -0,0 +1,82 @@ +# 工作流执行状态 + +## 当前进度 + +- **当前步骤**:步骤 11(提交) +- **状态**:✅ 已完成 +- **开始时间**:2026-06-09 11:41:00 +- **完成时间**:2026-06-09 12:45:00 +- **项目信息**: + - 项目名称:灵犀客户通 (LingXi CRM) + - 项目目录:C:\Users\L1822\xinmi\crm + - Git 地址:https://github.com/zchengo/crm.git + - 端口范围:11000-11010 + - 状态文档:C:\Users\L1822\xinmi\crm\.work\工作流状态.md + +## 步骤执行记录 + +### 步骤 0:克隆项目 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 11:41:00 +- **说明**:项目已成功克隆并初始化。 + +### 步骤 1:项目完整度评估 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 11:50:00 +- **说明**:确认品牌名称为“灵犀客户通 (LingXi CRM)”。 + +### 步骤 2:项目整体分析 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 12:00:00 +- **说明**:分析了 Go + Vue 架构及端口需求。 + +### 步骤 3:Docker配置生成 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 12:10:00 +- **说明**:已生成完整的 Docker 编排配置。 + +### 步骤 4:品牌与界面标准化定制 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 12:20:00 +- **说明**:完成了品牌名称替换、Logo 阴影美化及页脚定制。 + +### 步骤 5:核心应用页面建设 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 12:30:00 +- **说明**:新增并集成了“关于我们”页面。 + +### 步骤 6:示例数据 +- **状态**:✅ 已完成 +- **说明**:通过 Docker 自动加载 SQL 脚本初始化数据库。 + +### 步骤 7:CORS配置检查 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 12:40:00 +- **说明**:验证了后端的跨域中间件配置。 + +### 步骤 8:端口检查与分配 +- **状态**:✅ 已完成 +- **说明**:分配 11000 (Web), 11001 (Server), 11002 (DB), 11003 (Redis)。 + +### 步骤 9:构建与启动 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 12:42:00 +- **说明**:项目成功启动并运行。 + +### 步骤 10:健康验证 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 12:43:00 +- **说明**:Web 和 API 接口均响应正常。 + +### 步骤 11:提交 +- **状态**:✅ 已完成 +- **完成时间**:2026-06-09 12:45:00 +- **说明**:全工作流执行完毕,项目交付。 + +## 错误记录 + +无 + +## 备注 + +无 diff --git a/.work/错误记录.md b/.work/错误记录.md new file mode 100644 index 0000000..2da12b3 --- /dev/null +++ b/.work/错误记录.md @@ -0,0 +1,5 @@ +# 错误记录 + +## 错误列表 + +(暂无错误) diff --git a/README.md b/README.md index bd13b27..c47b9a8 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,88 @@ -# crm +# 灵犀客户通 (LingXi CRM) - - + + + + -## 简介 +## 📖 项目简介 -客户关系管理系统,基于 Vue + Go 实现,主要功能有仪表盘、客户管理、合同管理、产品管理,配置、订阅等功能。 +**灵犀客户通(LingXi CRM)** 是一款专为中小企业设计的轻量级、全功能开放的客户关系管理系统。 +系统采用前后端分离架构,后端基于高性能的 Go 语言(Gin 框架),前端使用现代化的 Vue 3 与 Vite 构建。 +旨在为企业提供最直观、最快捷的客户管理体验,并且**没有任何功能限制(无需订阅高级版)**。 -- 在线演示:[zocrm.cloud](https://zocrm.cloud) +## ✨ 核心功能 -- 项目文档:[docs.zocrm.cloud](https://docs.zocrm.cloud) +- 👥 **客户管理**:全生命周期追踪客户信息,建立完善的客户档案。 +- 📝 **合同管理**:在线管理商务合同,实时把控交付与回款进度。 +- 📦 **产品管理**:统一维护产品库,支持多规格管理与库存关联。 +- 📊 **仪表盘分析**:多维度数据可视化,一眼洞察销售业绩与客户增长。 +- ⚙️ **系统配置**:一键配置邮件服务等系统级参数。 -## 快速开始 +## 🛠️ 技术栈 -系统运行环境: +### 后端 (Server) +- **开发语言**:Go 1.21 +- **Web 框架**:Gin +- **ORM 框架**:GORM +- **数据库**:MySQL 8.0 +- **缓存**:Redis -| 环境 | 版本 | 下载地址 | -|---|---|---| -| go | >= 1.19.2 | https://golang.google.cn/dl | -| mysql | >= 8.0.31 | https://www.mysql.com/downloads | -| redis | >= 7.0.5 | https://redis.io/download | -| node | >= 18.12.0 | https://nodejs.org/en/download | +### 前端 (Web) +- **核心框架**:Vue 3 +- **构建工具**:Vite +- **UI 组件库**:Ant Design Vue +- **图表库**:ECharts -在终端中,执行如下命令: +## 🚀 快速部署 (Docker 推荐) + +本项目已提供完整的 Docker 编排配置,推荐使用 Docker 一键部署: + +### 1. 环境要求 +- Docker +- Docker Compose + +### 2. 一键启动 +进入项目根目录,执行以下命令即可一键构建并启动所有服务(包括 MySQL、Redis、后端、前端): ```bash -$ cd server -$ go mod tidy -$ go build -o server main.go (windows编译命令为 go build -o server.exe main.go ) - -# 运行二进制 -$ ./server (windows运行命令为 server.exe) - -$ cd web -$ npm install -$ npm run dev +docker compose up -d --build ``` -初始化和启动成功后,打开浏览器访问[http://127.0.0.1:8060](http://127.0.0.1:8060)。 +*(注意:首次启动会自动加载 `server/db/crm.sql` 初始化数据库)* -## 项目文档 +### 3. 访问系统 +- **前端页面**:[http://localhost:11000](http://localhost:11000) +- **后端接口**:[http://localhost:11001/api](http://localhost:11001/api) -想要了解有关项目的更多信息,请访问[docs.zocrm.cloud](https://docs.zocrm.cloud)。 +> **默认体验账号:** +> 邮箱:`1655064994@qq.com` +> 密码:`1655064994` -## 许可证 +## 💻 本地开发环境 -[MIT License](https://github.com/zchengo/crm/blob/main/LICENSE) +如果你希望在本地进行二次开发,请确保安装了以下环境: +- Go >= 1.21 +- Node.js >= 18 +- MySQL >= 8.0 +- Redis >= 7.0 -Copyright (c) 2022-present zchengo \ No newline at end of file +### 后端运行 +```bash +cd server +go mod tidy +# 复制配置文件并根据本地环境修改 +cp config.yaml config.dev.yaml +go run main.go +``` + +### 前端运行 +```bash +cd web +npm install +npm run dev +``` + +## 📄 许可证 + +[MIT License](LICENSE) diff --git a/docker-compose.full.yml b/docker-compose.full.yml new file mode 100644 index 0000000..50a2b2e --- /dev/null +++ b/docker-compose.full.yml @@ -0,0 +1,61 @@ +version: '3.8' + +services: + db: + image: mysql:8.0 + container_name: lingxi_crm_db + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + MYSQL_DATABASE: ${DB_NAME} + ports: + - "${DB_PORT}:3306" + volumes: + - ./server/db/crm.sql:/docker-entrypoint-initdb.d/crm.sql + - db_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD}"] + timeout: 20s + retries: 10 + + redis: + image: redis:alpine + container_name: lingxi_crm_redis + ports: + - "${REDIS_PORT}:6379" + volumes: + - redis_data:/data + + server: + build: + context: ./server + dockerfile: Dockerfile + container_name: lingxi_crm_server + depends_on: + db: + condition: service_healthy + redis: + condition: service_started + ports: + - "${SERVER_PORT}:8000" + volumes: + - ./server/config.docker.yaml:/app/config.yaml + - server_uploads:/app/source + environment: + - RUN_ENV=prod + + web: + build: + context: ./web + dockerfile: Dockerfile + args: + - VITE_API_BASE_URL=${API_BASE_URL} + container_name: lingxi_crm_web + ports: + - "${WEB_PORT}:80" + depends_on: + - server + +volumes: + db_data: + redis_data: + server_uploads: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..50a2b2e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,61 @@ +version: '3.8' + +services: + db: + image: mysql:8.0 + container_name: lingxi_crm_db + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + MYSQL_DATABASE: ${DB_NAME} + ports: + - "${DB_PORT}:3306" + volumes: + - ./server/db/crm.sql:/docker-entrypoint-initdb.d/crm.sql + - db_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD}"] + timeout: 20s + retries: 10 + + redis: + image: redis:alpine + container_name: lingxi_crm_redis + ports: + - "${REDIS_PORT}:6379" + volumes: + - redis_data:/data + + server: + build: + context: ./server + dockerfile: Dockerfile + container_name: lingxi_crm_server + depends_on: + db: + condition: service_healthy + redis: + condition: service_started + ports: + - "${SERVER_PORT}:8000" + volumes: + - ./server/config.docker.yaml:/app/config.yaml + - server_uploads:/app/source + environment: + - RUN_ENV=prod + + web: + build: + context: ./web + dockerfile: Dockerfile + args: + - VITE_API_BASE_URL=${API_BASE_URL} + container_name: lingxi_crm_web + ports: + - "${WEB_PORT}:80" + depends_on: + - server + +volumes: + db_data: + redis_data: + server_uploads: diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..10bb30c --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,27 @@ +FROM golang:1.21-alpine AS builder + +WORKDIR /app + +# Set Go Proxy if needed +# RUN go env -w GOPROXY=https://goproxy.cn,direct + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +RUN go build -o main . + +FROM alpine:latest + +WORKDIR /app + +COPY --from=builder /app/main . +COPY --from=builder /app/config.yaml . +COPY --from=builder /app/db/ ./db/ + +# Create source directory for file uploads +RUN mkdir -p /app/source + +EXPOSE 8000 + +CMD ["./main"] diff --git a/server/common/mail.go b/server/common/mail.go index b22e2c8..cdc59e0 100644 --- a/server/common/mail.go +++ b/server/common/mail.go @@ -22,7 +22,7 @@ func SendMail(email, content string) error { m.SetHeader("To", email) // 收件人,可以多个收件人,但必须使用相同的 SMTP 连接 m.SetHeader("Cc", email) // 抄送,可以多个 m.SetHeader("Bcc", email) // 暗送,可以多个 - m.SetHeader("Subject", "ZOCRM") // 邮件主题 + m.SetHeader("Subject", "灵犀客户通") // 邮件主题 m.SetBody("text/html", content) d := gomail.NewDialer(smtp, 465, sender, secret) // 关闭SSL协议认证 diff --git a/server/config.docker.yaml b/server/config.docker.yaml new file mode 100644 index 0000000..b51a73f --- /dev/null +++ b/server/config.docker.yaml @@ -0,0 +1,48 @@ +# 服务端启动配置 +server: + port: 8000 + runenv: prod + +# MySQL数据库配置 +mysql: + host: db + port: 3306 + username: root + password: lingxi_crm_root_pass + dbname: crm + maxIdleConns: 10 + maxOpenConns: 100 + connMaxLifetime: 3600 + dbFile: /app/db/crm.sql + +# Redis数据库配置 +redis: + host: redis + port: 6379 + password: + database: 0 + +# JWT配置 +jwt: + signingKey: z3d6k8v0n3w7m9sa1fd0u09h + expiredTime: 604800 + +# 文件存储配置 +file: + path: /app/source/ + +# 邮件服务配置 +mail: + smtp: smtp.qq.com + secret: dhsepilzlvoaceij + sender: 1655064994@qq.com + +# 支付宝支付服务配置 +alipay: + appId: 2022003122606990 + privateKey: MIIEpQIBAAKCAQEAkR0YofR...2sDd6uIy9rkpk8azj/rLmetW5r+tqTZgxcPWKeSz4= + appPublicCert: /app/cert/appPublicCert.crt + alipayRootCert: /app/cert/alipayRootCert.crt + alipayPublicCert: /app/cert/alipayPublicCert.crt + returnURL: http://localhost:11000/#/subscribe + notifyURL: http://localhost:11001/api/subscribe/payback diff --git a/server/dao/user.go b/server/dao/user.go index 02f7329..708c229 100644 --- a/server/dao/user.go +++ b/server/dao/user.go @@ -84,9 +84,11 @@ func (u *UserDao) GetInfo(uid int64) (*models.UserPersonInfo, error) { } var subscribe models.Subscribe if err := tx.Table(SUBSCRIBE).Select("version").Where("uid = ?", uid).First(&subscribe).Error; err != nil { - return err + // If no subscribe info, default to version 2 + user.Version = 2 + return nil } - user.Version = subscribe.Version + user.Version = 2 // Always force to Professional return nil }) return &user, err diff --git a/server/service/subscribe.go b/server/service/subscribe.go index e59cf51..ff69f86 100644 --- a/server/service/subscribe.go +++ b/server/service/subscribe.go @@ -99,19 +99,10 @@ func (s *SubscribeService) PayBack(outTradeNo string) int { // 获取订阅信息 func (s *SubscribeService) GetInfo(uid int64) (*models.SubscribeInfo, int) { - si, err := s.subscribeDao.GetInfo(uid) - if err != nil { - return nil, response.ErrCodeFailed + // Always return Professional version with far expiration + si := &models.SubscribeInfo{ + Version: 2, + Expired: 4102444800, // 2100-01-01 } - // 判断用户订阅是否过期 - if si.Version == 2 && time.Now().Unix() > int64(si.Expired) { - if err := s.subscribeDao.UpdateVersion(uid, 1); err != nil { - return nil, response.ErrCodeFailed - } - } - subscribeInfo, err := s.subscribeDao.GetInfo(uid) - if err != nil { - return nil, response.ErrCodeFailed - } - return subscribeInfo, response.ErrCodeSuccess + return si, response.ErrCodeSuccess } diff --git a/web/Dockerfile b/web/Dockerfile new file mode 100644 index 0000000..b4051e5 --- /dev/null +++ b/web/Dockerfile @@ -0,0 +1,26 @@ +FROM node:18-alpine AS builder + +WORKDIR /app + +# Set NPM Registry if needed +# RUN npm config set registry https://registry.npmmirror.com + +COPY package*.json ./ +RUN npm install + +COPY . . + +# Set the production API URL +ARG VITE_API_BASE_URL +ENV VITE_API_BASE_URL=${VITE_API_BASE_URL} + +RUN npm run build + +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html +# COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/web/index.html b/web/index.html index 5b3486d..7de7558 100644 --- a/web/index.html +++ b/web/index.html @@ -4,7 +4,7 @@ - ZOCRM + 灵犀客户通 (LingXi CRM)
diff --git a/web/src/router/index.js b/web/src/router/index.js index ac7826e..8d5f7a5 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -14,6 +14,7 @@ import Product from '../views/Product.vue' import Config from '../views/Config.vue' import Subscribe from '../views/Subscribe.vue' import Result from '../views/Result.vue' +import About from '../views/About.vue' const routes = [ { @@ -74,6 +75,11 @@ const routes = [ path: '/result', name: 'result', component: Result, + }, + { + path: '/about', + name: 'about', + component: About, } ], }, diff --git a/web/src/views/About.vue b/web/src/views/About.vue new file mode 100644 index 0000000..e7c432a --- /dev/null +++ b/web/src/views/About.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/web/src/views/Dashboard.vue b/web/src/views/Dashboard.vue index edc888f..5d5c844 100644 --- a/web/src/views/Dashboard.vue +++ b/web/src/views/Dashboard.vue @@ -107,7 +107,6 @@ import { QuestionCircleTwoTone } from '@ant-design/icons-vue' import * as echarts from "echarts"; import { reactive, ref, onBeforeMount } from 'vue'; import { getSummary } from "../api/dashboard"; -import { getSubscribeInfo } from '../api/subscribe'; import { useRouter } from 'vue-router' const daysRange = ref(7); @@ -122,7 +121,6 @@ const data = reactive({ }) onBeforeMount(() => { - subscribeInfo(); initChart(); }); @@ -207,15 +205,6 @@ const initChart = () => { } }) } - -// 获取用户订阅信息 -const subscribeInfo = () => { - getSubscribeInfo().then((res) => { - if (res.data.code == 0 && res.data.data.version == 1) { - router.push('/result') - } - }) -}