|
|
@@ -1,8 +1,10 @@
|
|
|
import dayjs from 'dayjs';
|
|
|
-import { Audit } from '../model/index.js';
|
|
|
+import { Audit, AuditFile } from '../model/index.js';
|
|
|
import { generateCaptcha, ipLocResFu } from '../util/index.js';
|
|
|
import resSend from '../util/resSend.js';
|
|
|
-import { isEnv } from '../config/config.default.js';
|
|
|
+import { isEnv, upStaticFileUrl } from '../config/config.default.js';
|
|
|
+import path from 'path';
|
|
|
+import { deleteFileSafely } from '../util/clearAll.js';
|
|
|
|
|
|
// 需要做定时器处理,防止短时间多次发送
|
|
|
let loginFlag: any = {};
|
|
|
@@ -12,6 +14,7 @@ export const clearAuditCode = () => {
|
|
|
};
|
|
|
|
|
|
const audit = {
|
|
|
+ // 展示端 获取code
|
|
|
getCode: async (req: any, res: any) => {
|
|
|
req.apiDescription = '展示端-获取验证码';
|
|
|
const clientIp = ipLocResFu(req);
|
|
|
@@ -28,7 +31,8 @@ const audit = {
|
|
|
res.type('svg');
|
|
|
res.send(captcha.data);
|
|
|
},
|
|
|
- saveAudit: async (req: any, res: any) => {
|
|
|
+ // 展示端 素材上传-提交
|
|
|
+ saveWebAudit: async (req: any, res: any) => {
|
|
|
const clientIp = ipLocResFu(req);
|
|
|
if (!loginFlag[clientIp]) loginFlag[clientIp] = { loginFlagCode: '' };
|
|
|
|
|
|
@@ -51,6 +55,244 @@ const audit = {
|
|
|
return resSend(res, 0, '素材上传成功', findObj);
|
|
|
} else return resSend(res, 400, '验证码错误');
|
|
|
},
|
|
|
+ // 后台管理-获取素材提交审核的列表
|
|
|
+ getAuditList: async (req: any, res: any) => {
|
|
|
+ req.apiDescription = '素材审核-获取素材审核列表';
|
|
|
+ const { pageNum = 1, pageSize = 10, searchKey = '' } = req.body;
|
|
|
+
|
|
|
+ // 构建查询条件
|
|
|
+ const query: any = {};
|
|
|
+
|
|
|
+ if (searchKey) {
|
|
|
+ // 使用正则表达式实现模糊查询,'i'表示不区分大小写
|
|
|
+ query.title = { $regex: searchKey, $options: 'i' };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算跳过的文档数量
|
|
|
+ const skip = (pageNum - 1) * pageSize;
|
|
|
+
|
|
|
+ // createTime降序
|
|
|
+ const sortCondition: any = { createTime: -1 };
|
|
|
+
|
|
|
+ // 并行执行:获取总条数和查询当前页数据
|
|
|
+ const [total, data] = await Promise.all([
|
|
|
+ // 获取满足条件的总记录数
|
|
|
+ Audit.countDocuments(query),
|
|
|
+ // 查询当前页数据
|
|
|
+ Audit.find(query).skip(skip).limit(parseInt(pageSize)).sort(sortCondition), // 按sort字段降序,数字越大越靠前;相同则按updateTime降序
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 计算总页数
|
|
|
+ const totalPages = Math.ceil(total / pageSize);
|
|
|
+ return resSend(res, 0, '获取素材审核列表成功', {
|
|
|
+ list: data,
|
|
|
+ pageNum: parseInt(pageNum),
|
|
|
+ pageSize: parseInt(pageSize),
|
|
|
+ total,
|
|
|
+ totalPages,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ delAudit: async (req: any, res: any) => {
|
|
|
+ const { _id } = req.params; // 从URL参数中获取ID
|
|
|
+ // 1. 根据ID查找数据
|
|
|
+ const info = await Audit.findById(_id);
|
|
|
+ if (!info) return resSend(res, 404, '_id错误或数据已被删除');
|
|
|
+
|
|
|
+ const deletedInfo: any = await Audit.findByIdAndDelete(_id);
|
|
|
+ req.apiDescription = `素材审核-删除素材审核数据-${deletedInfo.title}`;
|
|
|
+ return resSend(res, 0, '删除素材审核数据成功');
|
|
|
+ },
|
|
|
+ // 后台管理-通过id获取素材审核详情
|
|
|
+ getAuditInfo: async (req: any, res: any) => {
|
|
|
+ const { _id } = req.params;
|
|
|
+
|
|
|
+ if (!_id) return resSend(res, 400, '_id不能为空');
|
|
|
+
|
|
|
+ // 1. 根据ID查询商品基本信息
|
|
|
+ const findInfo = await Audit.findById(_id);
|
|
|
+ if (!findInfo) return resSend(res, 404, '_id错误或数据已被删除');
|
|
|
+
|
|
|
+ // 2. 如果 fileIds 存在且是非空数组,则查询关联的文件
|
|
|
+ let fileDetails: any[] = [];
|
|
|
+ if (findInfo.fileIds && findInfo.fileIds.length > 0) {
|
|
|
+ // 使用 $in 操作符批量查询 Files 集合
|
|
|
+ fileDetails = await AuditFile.find({
|
|
|
+ _id: { $in: findInfo.fileIds },
|
|
|
+ });
|
|
|
+ // 3. 按照 fileIds 数组的顺序对 fileDetails 进行排序
|
|
|
+ if (fileDetails.length > 0) {
|
|
|
+ // 创建一个映射,将文件的 _id 映射到文件对象
|
|
|
+ const fileMap = new Map();
|
|
|
+ fileDetails.forEach((file) => {
|
|
|
+ // 统一转为字符串进行比较
|
|
|
+ fileMap.set(file._id.toString(), file);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 按照 fileIds 的顺序重新构建 fileDetails 数组
|
|
|
+ const sortedFileDetails: any[] = [];
|
|
|
+ findInfo.fileIds.forEach((fileId) => {
|
|
|
+ const file = fileMap.get(fileId.toString());
|
|
|
+ if (file) {
|
|
|
+ sortedFileDetails.push(file);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 用排序后的数组替换原始查询结果
|
|
|
+ fileDetails = sortedFileDetails;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 将文件详情合并到返回结果中
|
|
|
+ const info = {
|
|
|
+ ...findInfo.toObject(), // 将Mongoose文档转为普通JS对象
|
|
|
+ fileDetails: fileDetails,
|
|
|
+ };
|
|
|
+
|
|
|
+ req.apiDescription = `素材审核-获取素材审核详情-${info.title}`;
|
|
|
+ return resSend(res, 0, '获取素材审核详情成功', info);
|
|
|
+ },
|
|
|
+ // 后台管理-素材审核-审核
|
|
|
+ saveAudit: async (req: any, res: any) => {
|
|
|
+ const { _id, auditArr = [] } = req.body;
|
|
|
+
|
|
|
+ if (!_id) return resSend(res, 400, '_id不能为空');
|
|
|
+
|
|
|
+ if (typeof auditArr !== 'object' || auditArr.length < 1)
|
|
|
+ return resSend(res, 404, '文件_id不能为空');
|
|
|
+
|
|
|
+ // 需求1:更新Audit表的state字段为true
|
|
|
+ const auditRecord = await Audit.findById(_id);
|
|
|
+ if (!auditRecord) return resSend(res, 404, '_id错误或数据已被删除');
|
|
|
+
|
|
|
+ if (auditRecord.state) return resSend(res, 404, '这条数据已经被审核了');
|
|
|
+
|
|
|
+ // 2. 并行执行两个独立的更新操作(替代事务)
|
|
|
+ const [auditUpdateResult, fileUpdateResult] = await Promise.all([
|
|
|
+ // 更新Audit表的状态
|
|
|
+ Audit.updateOne(
|
|
|
+ { _id: _id },
|
|
|
+ {
|
|
|
+ $set: {
|
|
|
+ state: true,
|
|
|
+ updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ },
|
|
|
+ }
|
|
|
+ ),
|
|
|
+ // 使用bulkWrite批量更新附件表(核心优化)
|
|
|
+ AuditFile.bulkWrite(
|
|
|
+ auditArr.map((item: any) => ({
|
|
|
+ updateOne: {
|
|
|
+ filter: { _id: item._id },
|
|
|
+ update: {
|
|
|
+ $set: {
|
|
|
+ state: true,
|
|
|
+ remark: item.remark,
|
|
|
+ title: auditRecord.title,
|
|
|
+ phone: auditRecord.phone,
|
|
|
+ operator: req.user.userName,
|
|
|
+ updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ auditId: _id,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // 可选:如果附件不存在则创建
|
|
|
+ // upsert: true
|
|
|
+ },
|
|
|
+ })),
|
|
|
+ { ordered: false } // 无序执行,即使某个操作失败也继续执行其他操作
|
|
|
+ ),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 3. 检查附件更新结果
|
|
|
+ let succeedCount = 0;
|
|
|
+ let failedCount = 0;
|
|
|
+
|
|
|
+ // bulkWrite结果分析
|
|
|
+ if (fileUpdateResult.modifiedCount !== undefined) {
|
|
|
+ succeedCount = fileUpdateResult.modifiedCount;
|
|
|
+ // 如果有upsert操作,还需要加上upsertedCount
|
|
|
+ failedCount = auditArr.length - succeedCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ req.apiDescription = `素材审核-审核-${auditRecord.title}`;
|
|
|
+ return resSend(res, 0, '审核成功', {
|
|
|
+ succeed: succeedCount,
|
|
|
+ failed: failedCount,
|
|
|
+ total: auditArr.length,
|
|
|
+ auditUpdated: auditUpdateResult.modifiedCount,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // --------------------------------------
|
|
|
+ // 后台管理-获取素材库列表(过滤只返回state 为 true的数据)
|
|
|
+ getLibraryList: async (req: any, res: any) => {
|
|
|
+ req.apiDescription = '素材审核-获取素材库列表';
|
|
|
+ const { pageNum = 1, pageSize = 10, searchKey = '', type = '' } = req.body;
|
|
|
+
|
|
|
+ // 构建查询条件
|
|
|
+ const query: any = {
|
|
|
+ state: true,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (type) query.type = type;
|
|
|
+
|
|
|
+ if (searchKey) {
|
|
|
+ // 使用正则表达式实现模糊查询,'i'表示不区分大小写
|
|
|
+ query.title = { $regex: searchKey, $options: 'i' };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算跳过的文档数量
|
|
|
+ const skip = (pageNum - 1) * pageSize;
|
|
|
+
|
|
|
+ // 修改排序逻辑:先按sort字段降序,再按updateTime字段降序
|
|
|
+ const sortCondition: any = { sort: -1, updateTime: -1 };
|
|
|
+
|
|
|
+ // 并行执行:获取总条数和查询当前页数据
|
|
|
+ const [total, data] = await Promise.all([
|
|
|
+ // 获取满足条件的总记录数
|
|
|
+ AuditFile.countDocuments(query),
|
|
|
+ // 查询当前页数据
|
|
|
+ AuditFile.find(query).skip(skip).limit(parseInt(pageSize)).sort(sortCondition), // 按sort字段降序,数字越大越靠前;相同则按updateTime降序
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 计算总页数
|
|
|
+ const totalPages = Math.ceil(total / pageSize);
|
|
|
+ return resSend(res, 0, '获取素材库列表成功', {
|
|
|
+ list: data,
|
|
|
+ pageNum: parseInt(pageNum),
|
|
|
+ pageSize: parseInt(pageSize),
|
|
|
+ total,
|
|
|
+ totalPages,
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 素材库-删除
|
|
|
+ delLibrary: async (req: any, res: any) => {
|
|
|
+ const { _id } = req.params; // 从URL参数中获取ID
|
|
|
+ // 1. 根据ID查找数据
|
|
|
+ const info = await AuditFile.findById(_id);
|
|
|
+ if (!info) return resSend(res, 404, '_id错误或数据已被删除');
|
|
|
+
|
|
|
+ const deletedInfo: any = await AuditFile.findByIdAndDelete(_id);
|
|
|
+
|
|
|
+ // 同时删除静态文件
|
|
|
+ if (deletedInfo.originalUrl) {
|
|
|
+ const fullOriginalPath = path.join(
|
|
|
+ isEnv ? process.cwd() : upStaticFileUrl,
|
|
|
+ '',
|
|
|
+ deletedInfo.originalUrl
|
|
|
+ );
|
|
|
+ deleteFileSafely(fullOriginalPath);
|
|
|
+ }
|
|
|
+ if (deletedInfo.compressedUrl) {
|
|
|
+ const fullOriginalPath = path.join(
|
|
|
+ isEnv ? process.cwd() : upStaticFileUrl,
|
|
|
+ '',
|
|
|
+ deletedInfo.compressedUrl
|
|
|
+ );
|
|
|
+ deleteFileSafely(fullOriginalPath);
|
|
|
+ }
|
|
|
+ req.apiDescription = `素材审核-删除素材库数据-${deletedInfo.title}`;
|
|
|
+ return resSend(res, 0, '删除素材库数据成功');
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
export default audit;
|