修正了更多功能

This commit is contained in:
2026-06-24 23:12:43 +08:00
parent 56af6ab269
commit b9266dfc1f
4 changed files with 1082 additions and 38 deletions

1070
API.md Normal file

File diff suppressed because it is too large Load Diff

29
db.js
View File

@@ -59,9 +59,9 @@ async function initDB() {
city VARCHAR(50) DEFAULT NULL COMMENT '市',
district VARCHAR(50) DEFAULT NULL COMMENT '区',
address VARCHAR(200) DEFAULT NULL COMMENT '详细地址',
customer_type VARCHAR(20) DEFAULT 'Normal' COMMENT '客户类型: VIP-地区总代理, Normal-普通代理',
email VARCHAR(100) DEFAULT NULL COMMENT '电子邮箱',
remark TEXT DEFAULT NULL COMMENT '备注信息',
responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID (用于数据范围隔离)',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
@@ -69,8 +69,6 @@ async function initDB() {
KEY idx_customers_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='加盟商信息表'
`)
// 确保 customer_type 列存在(兼容旧表)
try { await pool.query(`ALTER TABLE customers ADD COLUMN customer_type VARCHAR(20) DEFAULT 'Normal' COMMENT '客户类型: VIP-地区总代理, Normal-普通代理' AFTER address`) } catch {}
await pool.query(`
CREATE TABLE IF NOT EXISTS employees (
id INT NOT NULL AUTO_INCREMENT COMMENT '员工ID (主键)',
@@ -125,6 +123,8 @@ async function initDB() {
employee_id INT DEFAULT NULL COMMENT '业务员ID (关联员工表)',
status VARCHAR(20) NOT NULL DEFAULT '生效' COMMENT '合同状态: 草稿/生效/完成/作废',
remark TEXT DEFAULT NULL COMMENT '备注',
type VARCHAR(20) NOT NULL DEFAULT 'franchise' COMMENT '合同类型: franchise-加盟合同, supply-采购合同',
responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID (用于数据范围隔离)',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
@@ -143,6 +143,7 @@ async function initDB() {
handle_status VARCHAR(20) NOT NULL DEFAULT '待处理' COMMENT '处理状态: 待处理/处理中/已完成',
service_date DATE DEFAULT NULL COMMENT '售后日期',
remark TEXT DEFAULT NULL COMMENT '备注',
responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID (用于数据范围隔离)',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
@@ -203,27 +204,7 @@ async function initDB() {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商信息表'
`)
// ============ 2.4 旧表迁移(兼容已有数据库) ============
// users 表:删除 real_name、role新增 is_active、role_id、employee_id、department
try { await pool.query(`ALTER TABLE users DROP COLUMN real_name`) } catch {}
try { await pool.query(`ALTER TABLE users DROP COLUMN role`) } catch {}
try { await pool.query(`ALTER TABLE users CHANGE COLUMN status is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '账号状态: 0-禁用, 1-启用'`) } catch {}
try { await pool.query(`ALTER TABLE users ADD COLUMN role_id INT DEFAULT NULL COMMENT '角色ID' AFTER is_active`) } catch {}
try { await pool.query(`ALTER TABLE users ADD COLUMN employee_id INT DEFAULT NULL COMMENT '员工ID' AFTER role_id`) } catch {}
try { await pool.query(`ALTER TABLE users ADD COLUMN department VARCHAR(100) DEFAULT NULL COMMENT '所属部门' AFTER employee_id`) } catch {}
// customers 表:新增 responsible_user_id
try { await pool.query(`ALTER TABLE customers ADD COLUMN responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID'`) } catch {}
// contracts 表:新增 type
try { await pool.query(`ALTER TABLE contracts ADD COLUMN type VARCHAR(20) DEFAULT 'franchise' COMMENT '合同类型: franchise-加盟合同, supply-采购合同'`) } catch {}
// contracts 表:新增 responsible_user_id用于数据范围隔离
try { await pool.query(`ALTER TABLE contracts ADD COLUMN responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID'`) } catch {}
// after_sales 表:新增 responsible_user_id用于数据范围隔离
try { await pool.query(`ALTER TABLE after_sales ADD COLUMN responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID'`) } catch {}
// ============ 2.5 种子数据 ============
// ============ 2.4 种子数据 ============
await seedRolesAndPermissions()
await seedDefaultUsers()
}

View File

@@ -14,7 +14,7 @@ function pagination(query) {
async function list(req, res) {
try {
const { page, pageSize, offset } = pagination(req.query)
const { name, phone, province, city, customer_type } = req.query
const { name, phone, province, city } = req.query
let where = 'WHERE 1=1'
const params = []
@@ -45,10 +45,6 @@ async function list(req, res) {
where += ' AND city = ?'
params.push(city)
}
if (customer_type) {
where += ' AND customer_type = ?'
params.push(customer_type)
}
// 查总数
const [[{ total }]] = await pool.query(
@@ -109,7 +105,7 @@ async function detail(req, res) {
async function create(req, res) {
const {
name, phone, province, city, district,
address, customer_type, email, remark, responsible_user_id,
address, email, remark, responsible_user_id,
} = req.body || {}
if (!name) {
@@ -121,10 +117,10 @@ async function create(req, res) {
const ownerId = responsible_user_id || req.user.id
const [result] = await pool.query(
`INSERT INTO customers (name, phone, province, city, district, address, customer_type, email, remark, responsible_user_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
`INSERT INTO customers (name, phone, province, city, district, address, email, remark, responsible_user_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[name, phone || null, province || null, city || null, district || null,
address || null, customer_type || 'Normal', email || null, remark || null, ownerId]
address || null, email || null, remark || null, ownerId]
)
const [rows] = await pool.query('SELECT * FROM customers WHERE id = ?', [result.insertId])
res.json({ code: 0, message: 'ok', data: rows[0] })
@@ -139,7 +135,7 @@ async function update(req, res) {
const { id } = req.params
const fields = [
'name', 'phone', 'province', 'city', 'district',
'address', 'customer_type', 'email', 'remark', 'responsible_user_id',
'address', 'email', 'remark', 'responsible_user_id',
]
try {

View File

@@ -457,7 +457,7 @@ async function main() {
r = await req('POST', '/api/customers', {
name: 'CRUD测试加盟商', phone: '13800007777', province: '河南', city: '郑州',
customer_type: 'VIP', email: 'test@mixue.com',
email: 'test@mixue.com',
}, tokens.admin)
check('admin POST /api/customers (创建)', r, 200)
const custCrudId = r.body?.data?.id
@@ -478,9 +478,6 @@ async function main() {
r = await req('GET', '/api/customers?name=CRUD', null, tokens.admin)
check('GET /api/customers?name=CRUD (搜索)', r, 200)
r = await req('GET', '/api/customers?customer_type=VIP', null, tokens.admin)
check('GET /api/customers?customer_type=VIP (筛选)', r, 200)
}
r = await req('GET', '/api/customers/99999', null, tokens.admin)