修正了更多功能

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 '市', city VARCHAR(50) DEFAULT NULL COMMENT '市',
district VARCHAR(50) DEFAULT NULL COMMENT '区', district VARCHAR(50) DEFAULT NULL COMMENT '区',
address VARCHAR(200) DEFAULT NULL COMMENT '详细地址', address VARCHAR(200) DEFAULT NULL COMMENT '详细地址',
customer_type VARCHAR(20) DEFAULT 'Normal' COMMENT '客户类型: VIP-地区总代理, Normal-普通代理',
email VARCHAR(100) DEFAULT NULL COMMENT '电子邮箱', email VARCHAR(100) DEFAULT NULL COMMENT '电子邮箱',
remark TEXT 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 '创建时间', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id), PRIMARY KEY (id),
@@ -69,8 +69,6 @@ async function initDB() {
KEY idx_customers_phone (phone) KEY idx_customers_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='加盟商信息表' ) 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(` await pool.query(`
CREATE TABLE IF NOT EXISTS employees ( CREATE TABLE IF NOT EXISTS employees (
id INT NOT NULL AUTO_INCREMENT COMMENT '员工ID (主键)', id INT NOT NULL AUTO_INCREMENT COMMENT '员工ID (主键)',
@@ -125,6 +123,8 @@ async function initDB() {
employee_id INT DEFAULT NULL COMMENT '业务员ID (关联员工表)', employee_id INT DEFAULT NULL COMMENT '业务员ID (关联员工表)',
status VARCHAR(20) NOT NULL DEFAULT '生效' COMMENT '合同状态: 草稿/生效/完成/作废', status VARCHAR(20) NOT NULL DEFAULT '生效' COMMENT '合同状态: 草稿/生效/完成/作废',
remark TEXT DEFAULT NULL 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 '创建时间', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id), PRIMARY KEY (id),
@@ -143,6 +143,7 @@ async function initDB() {
handle_status VARCHAR(20) NOT NULL DEFAULT '待处理' COMMENT '处理状态: 待处理/处理中/已完成', handle_status VARCHAR(20) NOT NULL DEFAULT '待处理' COMMENT '处理状态: 待处理/处理中/已完成',
service_date DATE DEFAULT NULL COMMENT '售后日期', service_date DATE DEFAULT NULL COMMENT '售后日期',
remark TEXT 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 '创建时间', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id), PRIMARY KEY (id),
@@ -203,27 +204,7 @@ async function initDB() {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商信息表' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商信息表'
`) `)
// ============ 2.4 旧表迁移(兼容已有数据库) ============ // ============ 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 种子数据 ============
await seedRolesAndPermissions() await seedRolesAndPermissions()
await seedDefaultUsers() await seedDefaultUsers()
} }

View File

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

View File

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