feat: unlock all features and update README for LingXi CRM
CRM CI / build (push) Waiting to run

This commit is contained in:
yi
2026-06-09 15:59:52 +08:00
parent 0e9f2197cd
commit a205a7a49d
17 changed files with 569 additions and 67 deletions
+17
View File
@@ -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
+82
View File
@@ -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 架构及端口需求。
### 步骤 3Docker配置生成
- **状态**:✅ 已完成
- **完成时间**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 脚本初始化数据库。
### 步骤 7CORS配置检查
- **状态**:✅ 已完成
- **完成时间**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
- **说明**:全工作流执行完毕,项目交付。
## 错误记录
## 备注
+5
View File
@@ -0,0 +1,5 @@
# 错误记录
## 错误列表
(暂无错误)
+70 -32
View File
@@ -1,50 +1,88 @@
# crm
# 灵犀客户通 (LingXi CRM)
<a href="https://zocrm.cloud"><img src="https://img.shields.io/badge/在线演示-zocrm.cloud-%230092FF" /></a>
<a href="https://docs.zocrm.cloud"><img src="https://img.shields.io/badge/项目文档-docs.zocrm.cloud-%230092FF" /></a>
<a href="#"><img src="https://img.shields.io/badge/版本-v1.0.0-blue.svg" /></a>
<a href="#"><img src="https://img.shields.io/badge/Go-1.21-00ADD8.svg" /></a>
<a href="#"><img src="https://img.shields.io/badge/Vue-3.0-4FC08D.svg" /></a>
<a href="#"><img src="https://img.shields.io/badge/License-MIT-green.svg" /></a>
## 简介
## 📖 项目简介
客户关系管理系统,基于 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
### 后端运行
```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)
+61
View File
@@ -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:
+61
View File
@@ -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:
+27
View File
@@ -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"]
+1 -1
View File
@@ -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协议认证
+48
View File
@@ -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
+4 -2
View File
@@ -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
+5 -14
View File
@@ -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
}
+26
View File
@@ -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;"]
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./src/assets/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ZOCRM</title>
<title>灵犀客户通 (LingXi CRM)</title>
</head>
<body style="background-color: #FAFAFA;padding: 0;margin: 0;">
<div id="app"></div>
+6
View File
@@ -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,
}
],
},
+146
View File
@@ -0,0 +1,146 @@
<template>
<div class="about-container">
<a-card :bordered="false" class="about-card">
<div class="about-header">
<img src="../assets/logo.svg" class="about-logo" />
<h1 class="about-title">关于 灵犀客户通</h1>
<p class="about-subtitle">LingXi CRM - 极简高效智能的客户关系管理系统</p>
</div>
<a-divider />
<div class="about-section">
<h2>项目简介</h2>
<p>
灵犀客户通LingXi CRM是一款专为中小企业设计的轻量级客户关系管理系统
系统采用前后端分离架构后端基于高性能的 Go 语言Gin 框架前端使用现代化的 Vue 3 Vite 构建
旨在为企业提供最直观最快捷的客户管理体验
</p>
</div>
<div class="about-section">
<h2>核心功能</h2>
<a-row :gutter="[16, 16]">
<a-col :span="8">
<a-card hoverable size="small" title="客户管理">
全生命周期追踪客户信息建立完善的客户档案
</a-card>
</a-col>
<a-col :span="8">
<a-card hoverable size="small" title="合同管理">
在线管理商务合同实时把控交付与回款进度
</a-card>
</a-col>
<a-col :span="8">
<a-card hoverable size="small" title="产品管理">
统一维护产品库支持多规格管理与库存关联
</a-card>
</a-col>
<a-col :span="8">
<a-card hoverable size="small" title="仪表盘分析">
多维度数据可视化一眼洞察销售业绩与客户增长
</a-card>
</a-col>
<a-col :span="8">
<a-card hoverable size="small" title="角色权限">
精细化的权限控制确保企业数据安全可控
</a-card>
</a-col>
<a-col :span="8">
<a-card hoverable size="small" title="响应式设计">
完美适配多种终端随时随地处理业务需求
</a-card>
</a-col>
</a-row>
</div>
<div class="about-section">
<h2>技术栈</h2>
<a-tag color="blue">Go 1.21</a-tag>
<a-tag color="blue">Gin</a-tag>
<a-tag color="blue">GORM</a-tag>
<a-tag color="green">Vue 3</a-tag>
<a-tag color="green">Vite</a-tag>
<a-tag color="green">Ant Design Vue</a-tag>
<a-tag color="orange">MySQL 8.0</a-tag>
<a-tag color="red">Redis</a-tag>
<a-tag color="cyan">Docker</a-tag>
</div>
<div class="about-footer">
<p>版本信息v1.0.0 Stable</p>
<p> <a href="http://code.xinmi.cloud/" target="_blank">新觅源码库</a> 驱动</p>
</div>
</a-card>
</div>
</template>
<script setup>
</script>
<style scoped>
.about-container {
padding: 24px;
background: #f0f2f5;
min-height: 100%;
}
.about-card {
max-width: 900px;
margin: 0 auto;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.about-header {
text-align: center;
margin-bottom: 32px;
}
.about-logo {
width: 64px;
height: 64px;
margin-bottom: 16px;
filter: drop-shadow(0 4px 8px rgba(71, 111, 255, 0.3));
}
.about-title {
font-size: 28px;
font-weight: 600;
color: #1f1f1f;
margin-bottom: 8px;
}
.about-subtitle {
color: #8c8c8c;
font-size: 16px;
}
.about-section {
margin-bottom: 32px;
}
.about-section h2 {
font-size: 20px;
font-weight: 500;
border-left: 4px solid #476FFF;
padding-left: 12px;
margin-bottom: 16px;
}
.about-section p {
line-height: 1.8;
color: #595959;
}
.about-footer {
text-align: center;
margin-top: 48px;
color: #bfbfbf;
font-size: 14px;
}
.about-footer a {
color: #476FFF;
}
</style>
-11
View File
@@ -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')
}
})
}
</script>
<style scoped>
+9 -6
View File
@@ -4,7 +4,7 @@
<div class="logo">
<div><img src="../assets/logo.svg"
style="width: 32px;height: 32px;filter: drop-shadow(2px 2px 6px #79bbff);" /></div>
<div v-if="collapsed == false" class="title"><b>Z</b>O<b style="color: #1283FF;">C</b>RM</div>
<div v-if="collapsed == false" class="title">灵犀客户通</div>
</div>
<a-menu style="border-right: none;width: 149px;" v-model:selectedKeys="selectedKeys" mode="inline">
<a-menu-item :key="item.key" v-for="item in menuItem">
@@ -98,6 +98,9 @@
<component :is="Component" />
</router-view>
</transition>
<div style="text-align: center; padding: 15px; color: #999;">
© 2026 灵犀客户通 | <a href="http://code.xinmi.cloud/" target="_blank">新觅源码库</a>
</div>
</a-layout-content>
</a-layout>
</a-layout>
@@ -110,7 +113,7 @@ import { useStore } from '../store/index';
import { message } from 'ant-design-vue';
import { getUserInfo, getVerifyCode, userDelete } from '../api/user';
import { updateNotice, getNoticeCount, getNoticeList, deleteNotice } from '../api/notice';
import { DashboardOutlined, SmileOutlined, MehOutlined, ShoppingOutlined, ProfileOutlined, CrownOutlined } from '@ant-design/icons-vue';
import { DashboardOutlined, SmileOutlined, MehOutlined, ShoppingOutlined, ProfileOutlined, CrownOutlined, InfoCircleOutlined } from '@ant-design/icons-vue';
import { QuestionCircleFilled, BellFilled, ExclamationCircleOutlined, LogoutOutlined } from '@ant-design/icons-vue';
import moment from 'moment'
@@ -141,10 +144,10 @@ const menuItem = reactive([{
icon: ProfileOutlined,
name: "配置"
}, {
key: "subscribe",
to: "/subscribe",
icon: CrownOutlined,
name: "订阅"
key: "about",
to: "/about",
icon: InfoCircleOutlined,
name: "关于我们"
}])
// 表单校验