You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

492 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 会员控制器
*
* 功能:处理会员管理的业务逻辑
*/
const { Member, Coupon } = require('../models/Member');
const { validateRequired, validateBatch } = require('../utils/validator');
const { success } = require('../utils/response');
const { BusinessError, ValidationError, AuthorizationError } = require('../middleware/errorHandler');
// ========================================
// 会员相关控制器
// ========================================
/**
* 获取会员列表(分页、筛选)
* GET /api/members
* 需要管理员权限
*/
const getMembers = async (req, res, next) => {
try {
const {
page = 1,
pageSize = 10,
level,
search
} = req.query;
const result = await Member.findAll({
page: parseInt(page),
pageSize: parseInt(pageSize),
level,
search
});
res.json(success({
list: result.list,
pagination: {
total: result.total,
page: result.page,
pageSize: result.pageSize,
totalPages: Math.ceil(result.total / result.pageSize)
}
}, '获取会员列表成功'));
} catch (err) {
next(err);
}
};
/**
* 获取会员详情
* GET /api/members/:userId
*/
const getMemberByUserId = async (req, res, next) => {
try {
const { userId } = req.params;
// 权限控制:普通用户只能查看自己的会员信息
if (req.user.role === 'customer' && parseInt(userId) !== req.user.id) {
throw new AuthorizationError('您没有权限查看此会员信息');
}
const member = await Member.findByUserId(userId);
if (!member) {
throw new BusinessError('会员信息不存在', 404);
}
res.json(success(member, '获取会员详情成功'));
} catch (err) {
next(err);
}
};
/**
* 获取我的会员信息
* GET /api/members/my/info
*/
const getMyMemberInfo = async (req, res, next) => {
try {
let member = await Member.findByUserId(req.user.id);
// 如果用户还不是会员,自动创建会员资格
if (!member) {
member = await Member.create(req.user.id);
}
// 获取会员折扣信息
const discount = Member.getDiscount(member.level);
res.json(success({
...member,
discount,
discount_percent: Math.round((1 - discount) * 100) // 折扣百分比
}, '获取我的会员信息成功'));
} catch (err) {
next(err);
}
};
/**
* 创建会员
* POST /api/members
* 需要管理员权限
*/
const createMember = async (req, res, next) => {
try {
const { user_id } = req.body;
// 数据验证
const validation = validateRequired(user_id, '用户ID');
if (!validation.valid) {
throw new ValidationError('数据验证失败', validation.errors);
}
// 检查用户是否已是会员
const isMember = await Member.isMember(user_id);
if (isMember) {
throw new BusinessError('该用户已是会员');
}
const member = await Member.create(user_id);
res.status(201).json(success(member, '创建会员成功'));
} catch (err) {
next(err);
}
};
/**
* 更新会员等级
* PUT /api/members/:userId/level
* 需要管理员权限
*/
const updateMemberLevel = async (req, res, next) => {
try {
const { userId } = req.params;
const { level } = req.body;
// 数据验证
const validation = validateRequired(level, '会员等级');
if (!validation.valid) {
throw new ValidationError('数据验证失败', validation.errors);
}
// 验证等级值
const validLevels = ['bronze', 'silver', 'gold', 'diamond'];
if (!validLevels.includes(level)) {
throw new ValidationError(`会员等级必须是以下之一: ${validLevels.join(', ')}`);
}
const member = await Member.updateLevel(userId, level);
res.json(success(member, '更新会员等级成功'));
} catch (err) {
next(err);
}
};
/**
* 增加会员积分
* POST /api/members/:userId/points/add
* 需要管理员权限
*/
const addMemberPoints = async (req, res, next) => {
try {
const { userId } = req.params;
const { points, reason } = req.body;
// 数据验证
const validation = validateRequired(points, '积分数量');
if (!validation.valid) {
throw new ValidationError('数据验证失败', validation.errors);
}
if (parseInt(points) <= 0) {
throw new ValidationError('积分数量必须大于0');
}
const member = await Member.addPoints(
userId,
parseInt(points),
reason || '管理员手动添加'
);
res.json(success(member, '增加积分成功'));
} catch (err) {
next(err);
}
};
/**
* 扣减会员积分
* POST /api/members/:userId/points/deduct
* 需要管理员权限
*/
const deductMemberPoints = async (req, res, next) => {
try {
const { userId } = req.params;
const { points, reason } = req.body;
// 数据验证
const validation = validateRequired(points, '积分数量');
if (!validation.valid) {
throw new ValidationError('数据验证失败', validation.errors);
}
if (parseInt(points) <= 0) {
throw new ValidationError('积分数量必须大于0');
}
const member = await Member.deductPoints(
userId,
parseInt(points),
reason || '管理员手动扣减'
);
res.json(success(member, '扣减积分成功'));
} catch (err) {
next(err);
}
};
/**
* 获取会员统计信息
* GET /api/members/statistics
* 需要管理员权限
*/
const getMemberStatistics = async (req, res, next) => {
try {
const stats = await Member.getStatistics();
res.json(success(stats, '获取会员统计成功'));
} catch (err) {
next(err);
}
};
// ========================================
// 优惠券相关控制器
// ========================================
/**
* 获取优惠券列表
* GET /api/members/coupons
*/
const getCoupons = async (req, res, next) => {
try {
const {
page = 1,
pageSize = 10,
type,
is_active
} = req.query;
const result = await Coupon.findAll({
page: parseInt(page),
pageSize: parseInt(pageSize),
type,
is_active: is_active !== undefined ? parseInt(is_active) : undefined
});
res.json(success({
list: result.list,
pagination: {
total: result.total,
page: result.page,
pageSize: result.pageSize,
totalPages: Math.ceil(result.total / result.pageSize)
}
}, '获取优惠券列表成功'));
} catch (err) {
next(err);
}
};
/**
* 获取优惠券详情
* GET /api/members/coupons/:id
*/
const getCouponById = async (req, res, next) => {
try {
const { id } = req.params;
const coupon = await Coupon.findById(id);
if (!coupon) {
throw new BusinessError('优惠券不存在', 404);
}
res.json(success(coupon, '获取优惠券详情成功'));
} catch (err) {
next(err);
}
};
/**
* 创建优惠券
* POST /api/members/coupons
* 需要管理员权限
*/
const createCoupon = async (req, res, next) => {
try {
const {
name,
type,
discount_value,
min_amount,
max_discount,
valid_from,
valid_to,
total_quantity,
points_required,
is_active
} = req.body;
// 数据验证
const validation = validateBatch([
validateRequired(name, '优惠券名称'),
validateRequired(type, '优惠券类型'),
validateRequired(discount_value, '优惠值'),
validateRequired(valid_from, '有效期开始时间'),
validateRequired(valid_to, '有效期结束时间'),
validateRequired(total_quantity, '发放数量')
]);
if (!validation.valid) {
throw new ValidationError('数据验证失败', validation.errors);
}
// 验证优惠券类型
const validTypes = ['discount', 'cash'];
if (!validTypes.includes(type)) {
throw new ValidationError('优惠券类型必须是 discount 或 cash');
}
// 验证优惠值
if (parseFloat(discount_value) <= 0) {
throw new ValidationError('优惠值必须大于0');
}
if (type === 'discount' && parseFloat(discount_value) >= 1) {
throw new ValidationError('折扣类型的优惠值必须小于1如0.9表示9折');
}
// 验证数量
if (parseInt(total_quantity) <= 0) {
throw new ValidationError('发放数量必须大于0');
}
const coupon = await Coupon.create({
name,
type,
discount_value: parseFloat(discount_value),
min_amount: min_amount ? parseFloat(min_amount) : null,
max_discount: max_discount ? parseFloat(max_discount) : null,
valid_from,
valid_to,
total_quantity: parseInt(total_quantity),
points_required: points_required ? parseInt(points_required) : 0,
is_active: is_active !== undefined ? parseInt(is_active) : 1
});
res.status(201).json(success(coupon, '创建优惠券成功'));
} catch (err) {
next(err);
}
};
/**
* 更新优惠券
* PUT /api/members/coupons/:id
* 需要管理员权限
*/
const updateCoupon = async (req, res, next) => {
try {
const { id } = req.params;
const {
name,
discount_value,
min_amount,
max_discount,
valid_from,
valid_to,
total_quantity,
points_required,
is_active
} = req.body;
// 检查优惠券是否存在
const coupon = await Coupon.findById(id);
if (!coupon) {
throw new BusinessError('优惠券不存在', 404);
}
const updatedCoupon = await Coupon.update(id, {
name,
discount_value: discount_value !== undefined ? parseFloat(discount_value) : undefined,
min_amount: min_amount !== undefined ? (min_amount ? parseFloat(min_amount) : null) : undefined,
max_discount: max_discount !== undefined ? (max_discount ? parseFloat(max_discount) : null) : undefined,
valid_from,
valid_to,
total_quantity: total_quantity !== undefined ? parseInt(total_quantity) : undefined,
points_required: points_required !== undefined ? parseInt(points_required) : undefined,
is_active: is_active !== undefined ? parseInt(is_active) : undefined
});
res.json(success(updatedCoupon, '更新优惠券成功'));
} catch (err) {
next(err);
}
};
/**
* 删除优惠券
* DELETE /api/members/coupons/:id
* 需要管理员权限
*/
const deleteCoupon = async (req, res, next) => {
try {
const { id } = req.params;
// 检查优惠券是否存在
const coupon = await Coupon.findById(id);
if (!coupon) {
throw new BusinessError('优惠券不存在', 404);
}
await Coupon.delete(id);
res.json(success(null, '删除优惠券成功'));
} catch (err) {
next(err);
}
};
/**
* 领取优惠券
* POST /api/members/coupons/:id/claim
*/
const claimCoupon = async (req, res, next) => {
try {
const { id } = req.params;
const userCoupon = await Coupon.claimByUser(req.user.id, id);
res.status(201).json(success(userCoupon, '领取优惠券成功'));
} catch (err) {
next(err);
}
};
/**
* 获取我的优惠券
* GET /api/members/my/coupons
*/
const getMyCoupons = async (req, res, next) => {
try {
const { status } = req.query;
const coupons = await Coupon.getUserCoupons(req.user.id, status || null);
res.json(success({
count: coupons.length,
coupons
}, '获取我的优惠券成功'));
} catch (err) {
next(err);
}
};
// 导出所有控制器函数
module.exports = {
// 会员相关
getMembers,
getMemberByUserId,
getMyMemberInfo,
createMember,
updateMemberLevel,
addMemberPoints,
deductMemberPoints,
getMemberStatistics,
// 优惠券相关
getCoupons,
getCouponById,
createCoupon,
updateCoupon,
deleteCoupon,
claimCoupon,
getMyCoupons
};