虚拟试衣间-云尚衣橱小程序-衣橱管理实现

news2025/4/17 15:34:31

衣橱管理实现

目标 (Goal):

  • 用户 (User): 能通过 UniApp 小程序上传衣服图片。

  • 后端 (Backend): 接收图片,存到云存储,并将图片信息(URL、用户ID等)存入数据库。

  • 用户 (User): 能在小程序里看到自己上传的所有衣服图片列表。

技术栈细化 (Refined Tech Stack for this Phase):

  • 前端 (Frontend): UniApp (Vue 语法)

  • 后端 (Backend): Node.js, Express.js

  • 数据库 (Database): MongoDB (使用 Mongoose ODM 操作会更方便)

  • 图片存储 (Image Storage): 腾讯云 COS / 阿里云 OSS (必须使用云存储)

  • HTTP 请求库 (Frontend): UniApp 内置的 uni.request 或 uni.uploadFile

  • 文件上传处理 (Backend): multer 中间件

  • 云存储 SDK (Backend): tcb-admin-node (如果用腾讯云开发) 或 cos-nodejs-sdk-v5 (腾讯云 COS) 或 ali-oss (阿里云 OSS)

  • 认证 (Authentication): JWT (JSON Web Tokens)

1. 后端 (Node.js / Express) 实现

项目结构 (示例):

wardrobe-backend/
├── node_modules/
├── config/
│   ├── db.js         # 数据库连接
│   └── cloudStorage.js # 云存储配置 (密钥等) - 不要硬编码!用环境变量
├── controllers/
│   ├── authController.js
│   └── wardrobeController.js
├── middleware/
│   ├── authMiddleware.js  # JWT 验证
│   └── uploadMiddleware.js # Multer 配置
├── models/
│   ├── User.js         # Mongoose User Schema
│   └── Clothes.js      # Mongoose Clothes Schema
├── routes/
│   ├── authRoutes.js
│   └── wardrobeRoutes.js
├── .env              # 环境变量 (数据库URI, 云存储密钥, JWT Secret) - 加入 .gitignore
├── .gitignore
├── package.json
└── server.js         # Express 应用入口

关键代码实现点:

(a) server.js (入口文件)

require('dotenv').config(); // Load environment variables
const express = require('express');
const cors = require('cors');
const connectDB = require('./config/db');
const authRoutes = require('./routes/authRoutes');
const wardrobeRoutes = require('./routes/wardrobeRoutes');

// Connect to Database
connectDB();

const app = express();

// Middleware
app.use(cors()); // 允许跨域请求 (小程序需要)
app.use(express.json()); // 解析 JSON body
app.use(express.urlencoded({ extended: false })); // 解析 URL-encoded body

// Routes
app.use('/api/auth', authRoutes);
app.use('/api/wardrobe', wardrobeRoutes); // 衣橱相关路由

// Basic Error Handling (can be improved)
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send({ message: 'Something broke!', error: err.message });
});


const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

(b) models/User.js & models/Clothes.js (Mongoose Schemas)

// models/User.js
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
    openid: { type: String, required: true, unique: true },
    // session_key: { type: String, required: true }, // 按需存储,注意安全
    nickname: { type: String },
    avatarUrl: { type: String },
    // ... other fields
}, { timestamps: true });
module.exports = mongoose.model('User', UserSchema);

// models/Clothes.js
const mongoose = require('mongoose');
const ClothesSchema = new mongoose.Schema({
    userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true, index: true },
    imageUrl: { type: String, required: true }, // 图片在云存储的URL
    tags: { type: [String], default: [] },      // AI识别标签 (暂时为空或默认值)
    notes: { type: String, default: '' },      // 用户备注
    // ... 其他未来可能添加的字段 (颜色、类型等)
}, { timestamps: true });
module.exports = mongoose.model('Clothes', ClothesSchema);

(c) middleware/authMiddleware.js (JWT 验证)

const jwt = require('jsonwebtoken');
const User = require('../models/User');

const protect = async (req, res, next) => {
    let token;
    if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
        try {
            // Get token from header
            token = req.headers.authorization.split(' ')[1];

            // Verify token
            const decoded = jwt.verify(token, process.env.JWT_SECRET); // 使用环境变量中的密钥

            // Get user from the token (select -password if you store passwords)
            req.user = await User.findById(decoded.id).select('-session_key'); // 附加用户信息到 req

            if (!req.user) {
                 return res.status(401).json({ message: 'Not authorized, user not found' });
            }

            next();
        } catch (error) {
            console.error(error);
            res.status(401).json({ message: 'Not authorized, token failed' });
        }
    }

    if (!token) {
        res.status(401).json({ message: 'Not authorized, no token' });
    }
};

module.exports = { protect };

(d) routes/authRoutes.js & controllers/authController.js (登录逻辑)

// routes/authRoutes.js
const express = require('express');
const { loginUser } = require('../controllers/authController');
const router = express.Router();
router.post('/login', loginUser);
module.exports = router;

// controllers/authController.js
const axios = require('axios');
const jwt = require('jsonwebtoken');
const User = require('../models/User');

const WX_APPID = process.env.WX_APPID;       // 小程序 AppID
const WX_SECRET = process.env.WX_SECRET;   // 小程序 Secret

// Generate JWT
const generateToken = (id) => {
    return jwt.sign({ id }, process.env.JWT_SECRET, {
        expiresIn: '30d', // Token 有效期
    });
};

const loginUser = async (req, res) => {
    const { code, userInfo } = req.body; // 前端发送 wx.login 的 code 和用户信息

    if (!code) {
        return res.status(400).json({ message: 'Code is required' });
    }

    try {
        // 1. 用 code 换取 openid 和 session_key
        const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${WX_APPID}&secret=${WX_SECRET}&js_code=${code}&grant_type=authorization_code`;
        const response = await axios.get(url);
        const { openid, session_key } = response.data;

        if (!openid) {
            return res.status(400).json({ message: 'Failed to get openid', error: response.data });
        }

        // 2. 查找或创建用户
        let user = await User.findOne({ openid });
        if (!user) {
            // 如果需要用户信息&

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2330806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BGP路由协议之属性2

Orgin 起源 公认必遵属性 起源名称标记描述IGPi如果路由是由始发的 BGP 路由器使用 network 命令注入到 BGP 的,那么该 BGP 路由的 origin 属性为 IGPEGPe如果路由是通过 EGP 学习到的,那么该 BGP 路由的 Origin 属性为 EGPIncomplete?如果路由是通过…

纯个人整理,蓝桥杯使用的算法模板day2(0-1背包问题),手打个人理解注释,超全面,且均已验证成功(附带详细手写“模拟流程图”,全网首个

算法索引 01背包优化前空间优化版(使用一维数组)优化后的模拟流程图为何优化后,j不能使用正序遍历模拟流程图 代码对应实现案例 01背包 优化前 /*** 0-1背包问题解法(与下方代码表格示例对应,已模拟验证)*…

算法与数据结构线性表之栈和队列

Hello大家好&#xff01; 很高兴与大家见面&#xff01; 给生活添点快乐&#xff0c;开始今天的编程之路。 我的博客:<但愿. 我的专栏:C语言、题目精讲、算法与数据结构、C 欢迎点赞&#xff0c;关注 一 栈 1概念&#xff1a;栈是⼀种特殊的线性表&#xff0c;其只允许…

python应用之使用pdfplumber 解析pdf文件内容

目录标题 一. 通过 pdfplumber.open() 解析复杂PDF&#xff1a;1-2. 报错&#xff1a;V2 &#xff1a; 1-3. v3 使用tk 库&#xff0c;弹框选择文件运行环境准备完整代码保存运行测试步骤方式二&#xff1a;命令行方式&#xff08;适用于自动化&#xff09; 测试用例示例常见问…

Vue中使用antd-table组件实现数据选择、禁用、已选择禁用-demo

实现案例 实现过程 表格代码 关键代码 :row-selection="rowSelection" <div><div class="flex items-center justify-between pt-[24px] pb-[16px]"><p>已选:{{ keysNum }}</p><a-input-search v-model:value="productN…

C语言--统计输入字符串中的单词个数

输入 输入&#xff1a;大小写字母以及空格&#xff0c;单词以空格分隔 输出&#xff1a;单词个数 代码 如果不是空格且inWord0说明是进入单词的第一个字母&#xff0c;则单词总数加一。 如果是空格&#xff0c;证明离开单词&#xff0c;inWord 0。 #include <stdio.h&g…

Kubernetes 集群搭建(三):使用dashboard用户界面(需要访问外网获取yaml)

&#xff08;一&#xff09;简介 K8s Dashboard是Kubernetes提供的一种基于Web的用户界面工具&#xff0c;用于可视化地管理和监控Kubernetes集群 主要功能&#xff1a; 资源查看与管理&#xff1a; 查看Kubernetes集群中的各种资源&#xff0c;如节点、Pod、服务、部署等。 对…

Debian 12 服务器搭建Beego环境

一、Debian 12系统准备 1.更新系统 #apt update && apt upgrade -y 2.安装基础工具 #apt install -y git curl wget make gcc 二、安装Go环境 Go语言的镜像官网&#xff1a;https://golang.google.cn/ 1.下载go最新版 #cd /usr/local/src #wget -o https://golang.go…

游戏引擎学习第208天

运行游戏并回顾我们的情况 今天&#xff0c;我们将继续完成之前中断的调试输出工作。最近的工作偏离了一些&#xff0c;展示了如何进行元编程的实践&#xff0c;主要涉及了一个小的解析器。尽管这个解析器本身是一个玩具&#xff0c;但它展示了如何完成一个完整的循环&#xf…

【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的监控:使用 Actuator 实现健康检查

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、引子&…

蓝桥杯—数字接龙(dfs+减枝)

一.题目 二.思路 一看就是迷宫问题的变种&#xff0c;从左上角到达右下角&#xff0c;要解决 1.8个方向的方向向量&#xff0c;用dx&#xff0c;dy数组代表方向向量 2.要按照一个规律的数值串进行搜索0&#xff0c;1&#xff0c;2&#xff0c;k-1&#xff0c;0&#xff0c;1…

Docker与VNC的使用

https://hub.docker.com/r/dorowu/ubuntu-desktop-lxde-vnc 下载nvc 客户端 https://downloads.realvnc.com/download/file/viewer.files/VNC-Viewer-7.12.0-Windows.exe 服务端 docker pull dorowu/ubuntu-desktop-lxde-vnc#下载成功 docker pull dorowu/ubuntu-desktop-l…

C++——清明

#include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory> #include <ctime>using namespace std;class Weapon; // 前置声明class Hero{ pr…

手搓多模态-06 数据预处理

前情回顾 我们目前实现了视觉模型的编码器部分&#xff0c;然而&#xff0c;我们所做的是把一张图片编码嵌入成了许多个上下文相关的嵌入向量&#xff0c;然而我们期望的是一张图片用一个向量来表示&#xff0c;从而与文字的向量做点积形成相似度&#xff08;参考手搓多模态-01…

HCIP【路由过滤技术(详解)】

目录 1 简介 2 路由过滤方法 3 路由过滤工具 3.1 静默接口 3.2 ACL 3.3 地址前缀列表 3.4 filter-policy 3.4.1 filter-policy过滤接收路由&#xff08;以RIP为例&#xff09; 3.4.2 filter-policy过滤接收路由&#xff08;以OSPF为例&#xff09; 1 简介 路由过滤技术…

【AI插件开发】Notepad++ AI插件开发实践(代码篇):从Dock窗口集成到功能菜单实现

一、引言 上篇文章已经在Notepad的插件开发中集成了选中即问AI的功能&#xff0c;这一篇文章将在此基础上进一步集成&#xff0c;支持AI对话窗口以及常见的代码功能菜单&#xff1a; 显示AI的Dock窗口&#xff0c;可以用自然语言向 AI 提问或要求执行任务选中代码后使用&…

Vue3在ZKmall开源商城前端的应用实践与技术创新

ZKmall开源商城作为一款企业级电商解决方案&#xff0c;其前端架构基于Vue3实现了高效、灵活的开发模式&#xff0c;结合响应式设计、组件化开发与全链路性能优化&#xff0c;为多端协同和复杂业务场景提供了先进的技术支持。以下从技术架构、核心特性、性能优化等维度解析Vue3…

SpringAI+MCP协议 实战

文章目录 前言快速实战Spring AISpring AI 集成 MCP 协议Spring Mcp Client 示例Spring Mcp Server 示例 前言 尽管Python最近成为了编程语言的首选&#xff0c;但是Java在人工智能领域的地位同样不可撼动&#xff0c;得益于强大的Spring框架。随着人工智能技术的快速发展&…

[数据结构]图krusakl算法实现

目录 Kruskal算法 Kruskal算法 我们要在连通图中去找生成树 连通图&#xff1a;在无向图中&#xff0c;若从顶点v1到顶点v2有路径&#xff0c;则称顶点v1与顶点v2是连通的。如果图中任意一对顶点都是连通的&#xff0c;则称此图为连通图。 生成树&#xff1a;一个连通图的最小…

QEMU学习之路(5)— 从0到1构建Linux系统镜像

QEMU学习之路&#xff08;5&#xff09;— 从0到1构建Linux系统镜像 一、前言 参考&#xff1a;从内核到可启动镜像&#xff1a;0到1构建你的极简Linux系统 二、linux源码获取 安装编译依赖 sudo apt install -y build-essential libncurses-dev flex bison libssl-dev li…