shaogen1995 недель назад: 2
Родитель
Сommit
5929a4e8e8

+ 56 - 0
src/controller/auditController.ts

@@ -0,0 +1,56 @@
+import dayjs from 'dayjs';
+import { Audit } from '../model/index.js';
+import { generateCaptcha, ipLocResFu } from '../util/index.js';
+import resSend from '../util/resSend.js';
+import { isEnv } from '../config/config.default.js';
+
+//  需要做定时器处理,防止短时间多次发送
+let loginFlag: any = {};
+
+export const clearAuditCode = () => {
+  loginFlag = {};
+};
+
+const audit = {
+  getCode: async (req: any, res: any) => {
+    req.apiDescription = '展示端-获取验证码';
+    const clientIp = ipLocResFu(req);
+
+    const captcha = generateCaptcha();
+    // 将验证码文本存入session(小写以便不区分大小写校验)
+    const captchaTxt = captcha.text.toLowerCase();
+    if (loginFlag[clientIp]) loginFlag[clientIp].loginFlagCode = captchaTxt;
+    else loginFlag[clientIp] = { loginFlagCode: captchaTxt };
+
+    // console.log('生成的验证码(开发调试用):', captchaTxt); // 调试时可查看
+
+    // 设置响应头,告诉浏览器这是SVG图片
+    res.type('svg');
+    res.send(captcha.data);
+  },
+  saveAudit: async (req: any, res: any) => {
+    const clientIp = ipLocResFu(req);
+    if (!loginFlag[clientIp]) loginFlag[clientIp] = { loginFlagCode: '' };
+
+    const codeTxt = req.body.code.toLowerCase();
+
+    if (loginFlag[clientIp].loginFlagCode === codeTxt || (isEnv && codeTxt === '1111')) {
+      const fileIds: string[] = req.body.fileIds || [];
+
+      if (typeof fileIds !== 'object' || fileIds.length < 1)
+        return resSend(res, 404, '至少上传一个文件');
+
+      const infoModel = new Audit(req.body);
+      infoModel.createTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
+      infoModel.updateTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
+      // 保存数据到数据库
+      const dbBack = await infoModel.save();
+      // 将文档转换为普通对象
+      const findObj = dbBack.toObject();
+      req.apiDescription = `展示端-素材上传-${findObj.title}`;
+      return resSend(res, 0, '素材上传成功', findObj);
+    } else return resSend(res, 400, '验证码错误');
+  },
+};
+
+export default audit;

+ 2 - 3
src/controller/fileController.ts

@@ -3,10 +3,9 @@ import path from 'path';
 import resSend from '../util/resSend.js';
 import { isImageFile } from '../middleware/fileUpload.js';
 import { compressImage } from '../util/imageCompressor.js';
-import { Files } from '../model/index.js';
 
 const file = {
-  upload: async (req: any, res: any) => {
+  upload: async (req: any, res: any, MyModel: any) => {
     if (!req.file) return resSend(res, 404, '请选择要上传的文件');
 
     const { isDb, type: myType } = req.body;
@@ -46,7 +45,7 @@ const file = {
 
     // 如果需要存储到数据库
     if (isDb === 'true') {
-      const fileRecord = new Files({
+      const fileRecord = new MyModel({
         ...result,
         filename: path.basename(file.filename),
         mimetype: file.mimetype,

+ 2 - 1
src/controller/inedx.ts

@@ -1,5 +1,6 @@
 import user from './userController.js';
 import issue from './issueController.js';
 import file from './fileController.js';
+import audit from './auditController.js';
 
-export { user, issue, file };
+export { user, issue, file, audit };

+ 5 - 5
src/controller/issueController.ts

@@ -167,17 +167,17 @@ const issue = {
     });
   },
   saveGoods: async (req: any, res: any) => {
+    const fileType: string[] = req.body.fileType || [];
+
+    if (typeof fileType !== 'object' || fileType.length < 1)
+      return resSend(res, 404, '请选择文件类型');
+
     if (req.body._id) {
       // 编辑展品展示
       // 检查数据是否存在
       const existing: any = await Goods.findById(req.body._id);
       if (!existing) return resSend(res, 404, '数据不存在');
 
-      const fileType: string[] = req.body.fileType || [];
-
-      if (typeof fileType !== 'object' || fileType.length < 1)
-        return resSend(res, 404, '请选择文件类型');
-
       // 更新字段
       // 过滤一些字段
       const filetStr: string[] = [];

+ 10 - 0
src/middleware/validator/auditValidator.ts

@@ -0,0 +1,10 @@
+import { body } from 'express-validator';
+import errorBack from './errorBack.js';
+
+// web端素材上传
+export const auditUserVali = errorBack([
+  body('title').notEmpty().withMessage('标题不能为空'),
+  body('phone').notEmpty().withMessage('联系方式不能为空'),
+  body('description').notEmpty().withMessage('概述不能为空'),
+  body('code').notEmpty().withMessage('验证码不能为空'),
+]);

+ 5 - 0
src/model/advancedModel.ts

@@ -27,6 +27,11 @@ const AdvancedSchema = new mongoose.Schema({
     type: String,
     default: '',
   },
+  // 简介
+  intro: {
+    type: String,
+    default: '',
+  },
   // 内容
   description: {
     type: String,

+ 44 - 0
src/model/auditFileModel.ts

@@ -0,0 +1,44 @@
+import dayjs from 'dayjs';
+import mongoose from 'mongoose';
+
+const auditFileSchema = new mongoose.Schema({
+  filename: {
+    type: String,
+    required: true,
+  },
+  originalName: {
+    type: String,
+    required: true,
+  },
+  mimetype: {
+    type: String,
+    required: true,
+  },
+  type: String,
+  size: {
+    type: Number,
+    required: true,
+  },
+  isSelect: {
+    type: Boolean,
+    required: false,
+  },
+  compressedSize: {
+    type: Number,
+  },
+  originalUrl: {
+    type: String,
+    required: true,
+  },
+  compressedUrl: {
+    type: String,
+    default: '',
+  },
+
+  updateTime: {
+    type: String,
+    default: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+  },
+});
+
+export default auditFileSchema;

+ 36 - 0
src/model/auditModel.ts

@@ -0,0 +1,36 @@
+import dayjs from 'dayjs';
+import mongoose from 'mongoose';
+
+const auditSchema = new mongoose.Schema({
+  // 标题
+  title: {
+    type: String,
+    require: true,
+  },
+  // 联系方式
+  phone: {
+    type: String,
+    require: true,
+  },
+  // 概述
+  description: {
+    type: String,
+    require: true,
+  },
+  // 附件id集合
+  fileIds: {
+    type: [String],
+    default: [],
+  },
+
+  createTime: {
+    type: String,
+    default: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+  },
+  updateTime: {
+    type: String,
+    default: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+  },
+});
+
+export default auditSchema;

+ 2 - 2
src/model/dictModel.ts

@@ -1,7 +1,7 @@
 import dayjs from 'dayjs';
 import mongoose from 'mongoose';
 
-const dictModel = new mongoose.Schema({
+const dictSchema = new mongoose.Schema({
   name: {
     type: String,
     require: true,
@@ -21,4 +21,4 @@ const dictModel = new mongoose.Schema({
   },
 });
 
-export default dictModel;
+export default dictSchema;

+ 2 - 2
src/model/fileModel.ts

@@ -1,7 +1,7 @@
 import dayjs from 'dayjs';
 import mongoose from 'mongoose';
 
-const fileModel = new mongoose.Schema({
+const fileSchema = new mongoose.Schema({
   filename: {
     type: String,
     required: true,
@@ -37,4 +37,4 @@ const fileModel = new mongoose.Schema({
   },
 });
 
-export default fileModel;
+export default fileSchema;

+ 8 - 4
src/model/index.ts

@@ -2,12 +2,14 @@ import mongoose from 'mongoose';
 import userSchema from './userModel.js';
 import logSchema from './logModel.js';
 import { mongodbUrl } from '../config/config.default.js';
-import dictModel from './dictModel.js';
-import fileModel from './fileModel.js';
 import VideoSchema from './videlModel.js';
 import GoodsSchema from './goodsModel.js';
 import AdvancedSchema from './advancedModel.js';
 import ShareSchema from './shareModel.js';
+import auditSchema from './auditModel.js';
+import auditFileSchema from './auditFileModel.js';
+import dictSchema from './dictModel.js';
+import fileSchema from './fileModel.js';
 
 const main = async () => {
   await mongoose.connect(mongodbUrl);
@@ -23,9 +25,11 @@ main()
 
 export const User = mongoose.model('User', userSchema);
 export const Log = mongoose.model('Log', logSchema);
-export const Dict = mongoose.model('Dict', dictModel);
-export const Files = mongoose.model('File', fileModel);
+export const Dict = mongoose.model('Dict', dictSchema);
+export const Files = mongoose.model('File', fileSchema);
 export const Video = mongoose.model('video', VideoSchema);
 export const Goods = mongoose.model('goods', GoodsSchema);
 export const Advanced = mongoose.model('advanced', AdvancedSchema);
 export const Share = mongoose.model('share', ShareSchema);
+export const AuditFile = mongoose.model('auditFile', auditFileSchema);
+export const Audit = mongoose.model('audit', auditSchema);

+ 4 - 1
src/router/file.ts

@@ -4,9 +4,12 @@ import { verifyToken } from '../middleware/jwt.js';
 // 记录日志的中间件
 import { file } from '../controller/inedx.js';
 import { uploadZhong } from '../middleware/fileUpload.js';
+import { Files } from '../model/index.js';
 
 const fileRouter = express.Router();
 
-fileRouter.post('/upload', verifyToken, uploadZhong.single('file'), file.upload);
+fileRouter.post('/upload', verifyToken, uploadZhong.single('file'), (req, res) => {
+  return file.upload(req, res, Files);
+});
 
 export default fileRouter;

+ 14 - 2
src/router/show.ts

@@ -1,5 +1,8 @@
 import express from 'express';
-import { issue } from '../controller/inedx.js';
+import { issue, audit, file } from '../controller/inedx.js';
+import { auditUserVali } from '../middleware/validator/auditValidator.js';
+import { uploadZhong } from '../middleware/fileUpload.js';
+import { AuditFile } from '../model/index.js';
 
 const showRouter = express.Router();
 
@@ -25,6 +28,15 @@ showRouter
   // ----------------------------------
   // 获取素材共享列表
   .post('/getShareList', issue.getShareList)
-  .get('/getShareInfo/:_id', issue.getShareInfo);
+  .get('/getShareInfo/:_id', issue.getShareInfo)
+  // ----------------------------------展示端
+  //获取验证码
+  .get('/getCode', audit.getCode)
+  // 素材上传-保存
+  .post('/saveAudit', auditUserVali, audit.saveAudit)
+  // 上传文件
+  .post('/upload', uploadZhong.single('file'), (req, res) => {
+    return file.upload(req, res, AuditFile);
+  });
 
 export default showRouter;

+ 7 - 5
src/util/clearAll.ts

@@ -6,11 +6,12 @@ import { Files, Goods, Share } from '../model/index.js';
 import { clearLoginCode } from '../controller/userController.js';
 import { isEnv, upStaticFileUrl, writeLogUrl } from '../config/config.default.js';
 import { AddTxtFileFu } from './index.js';
+import { clearAuditCode } from '../controller/auditController.js';
 
 /**
  * 增强版清理函数:删除孤立Files记录及对应的静态文件
  */
-export const clearFileFu = async (Model: any) => {
+export const clearFileFu = async (Model: any, FileModel: any) => {
   try {
     console.log('开始清理孤立的files数据和静态文件...');
 
@@ -22,7 +23,7 @@ export const clearFileFu = async (Model: any) => {
     const usedIdsArray = usedFileIds.length > 0 ? usedFileIds[0].ids : [];
 
     // 2. 查找需要删除的孤立文件记录
-    const filesToDelete = await Files.find({
+    const filesToDelete = await FileModel.find({
       _id: { $nin: usedIdsArray },
     });
 
@@ -35,7 +36,7 @@ export const clearFileFu = async (Model: any) => {
 
     // 3. 并行删除静态文件,并处理结果
     const fileDeletionResults = await Promise.allSettled(
-      filesToDelete.map(async (fileDoc) => {
+      filesToDelete.map(async (fileDoc: any) => {
         const deletionPromises = [];
 
         // 处理 originalUrl
@@ -140,10 +141,11 @@ export const clearAllFu = () => {
     () => {
       console.log('执行定时任务...');
       // 清理 多个集合中 有fileIds 没有使用的file文件
-      clearFileFu(Goods);
-      clearFileFu(Share);
+      clearFileFu(Goods, Files);
+      clearFileFu(Share, Files);
       // 清理用户登录的验证码对象
       clearLoginCode();
+      clearAuditCode();
     },
     {
       timezone: 'Asia/Shanghai', // 根据你的服务器时区设置