基于SSM框架的在线美甲商城系统 - 源码深度解析

JavaJavaScriptHTMLCSSSSM框架JSP+ServletMySQL
2026-02-0815 浏览

文章摘要

本系统是一款基于SSM(Spring + Spring MVC + MyBatis)框架技术栈构建的B2C在线美甲产品交易平台。它旨在解决传统美甲产品零售渠道单一、信息不透明、购物流程繁琐等核心痛点。通过整合商品展示、在线下单、支付结算等核心模块,系统为美甲爱好者、个人用户及小型美甲工作室提供了一个...

在美妆行业数字化浪潮中,专业美甲产品的线上化销售成为提升产业链效率的关键环节。传统美甲产品采购存在渠道有限、价格不透明、仓储管理成本高等痛点,亟需一个集商品展示、在线交易、库存管理于一体的专业化B2C解决方案。美甲臻选电商平台应运而生,通过SSM(Spring+Spring MVC+MyBatis)技术栈构建的高效架构,为美甲师、沙龙经营者和DIY爱好者提供一站式采购服务。

系统架构与技术栈设计

该平台采用经典的三层架构模式,各层职责分明且耦合度低。表现层基于Spring MVC框架实现请求路由和视图渲染,通过@Controller注解声明业务端点,配合JSP视图技术实现动态页面生成。业务逻辑层由Spring IoC容器统一管理服务组件,使用@Service标注业务实现类,并通过@Transactional注解确保订单创建、库存更新等核心操作的原子性。数据持久层选用MyBatis框架,通过XML映射文件实现对象关系映射,灵活控制SQL执行逻辑。

技术选型方面,后端核心采用Java 8语言特性,利用Stream API处理集合数据,Lambda表达式简化代码结构。前端采用原生HTML/CSS/JavaScript组合,保证跨浏览器兼容性。数据库使用MySQL 5.7,采用InnoDB存储引擎保障事务安全。项目通过Maven进行依赖管理,规范第三方库版本控制。

数据库架构深度解析

商品分类体系的优化设计

分类表采用层级化设计,通过cateid主键建立商品与类目的关联关系。值得关注的是memo字段的灵活运用,不仅存储分类描述信息,还通过JSON格式扩展存储分类属性模板:

CREATE TABLE `cate` (
  `cateid` varchar(255) NOT NULL COMMENT '分类ID',
  `catename` varchar(255) DEFAULT NULL COMMENT '分类名称',
  `memo` varchar(255) DEFAULT NULL COMMENT '备注',
  `addtime` varchar(255) DEFAULT NULL COMMENT '添加时间',
  PRIMARY KEY (`cateid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分类表'

在实际查询中,系统通过复合索引优化分类页面的加载速度:

CREATE INDEX idx_cate_time ON cate(addtime, cateid);
CREATE INDEX idx_cate_name ON cate(catename(20));

商品核心数据模型设计

美甲商品表的设计体现了电商系统的典型特征,包含销售统计、时效控制、推荐策略等商业逻辑字段:

CREATE TABLE `meijia` (
  `meijiaid` varchar(255) NOT NULL COMMENT '美甲ID',
  `meijianame` varchar(255) DEFAULT NULL COMMENT '美甲名称',
  `image` varchar(255) DEFAULT NULL COMMENT '图片',
  `cateid` varchar(255) DEFAULT NULL COMMENT '分类ID',
  `price` varchar(255) DEFAULT NULL COMMENT '价格',
  `recommend` varchar(255) DEFAULT NULL COMMENT '推荐',
  `thestart` varchar(255) DEFAULT NULL COMMENT '开始时间',
  `theend` varchar(255) DEFAULT NULL COMMENT '结束时间',
  `hits` varchar(255) DEFAULT NULL COMMENT '点击量',
  `sellnum` varchar(255) DEFAULT NULL COMMENT '销售数量',
  `contents` varchar(6000) DEFAULT NULL COMMENT '内容',
  PRIMARY KEY (`meijiaid`),
  KEY `fk_cate` (`cateid`),
  KEY `idx_recommend` (`recommend`),
  KEY `idx_sellnum` (`sellnum`),
  KEY `idx_time_range` (`thestart`, `theend`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='美甲表'

其中contents字段采用varchar(6000)类型,平衡了文本存储需求与查询性能。价格字段使用varchar类型支持灵活的价格格式,同时通过应用层验证确保数值有效性。

配送网络的地理化建模

配送员表与城市表的关联设计实现了区域化配送管理:

CREATE TABLE `peihuo` (
  `peihuoid` varchar(255) NOT NULL COMMENT '配送员ID',
  `peihuoname` varchar(255) DEFAULT NULL COMMENT '配送员姓名', 
  `cityid` varchar(255) DEFAULT NULL COMMENT '城市ID',
  `address` varchar(255) DEFAULT NULL COMMENT '地址',
  `contact` varchar(255) DEFAULT NULL COMMENT '联系方式',
  `memo` varchar(255) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`peihuoid`),
  KEY `fk_city` (`cityid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='配送员表'

CREATE TABLE `city` (
  `cityid` varchar(255) NOT NULL COMMENT '城市ID',
  `cityname` varchar(255) DEFAULT NULL COMMENT '城市名称',
  PRIMARY KEY (`cityid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='城市表'

这种设计支持按城市分配订单、优化配送路径,为后续扩展同城速递功能奠定基础。

核心业务逻辑实现

商品详情页的动态渲染

商品详情页面通过Spring MVC控制器接收商品ID参数,查询并组装商品数据模型:

@Controller
@RequestMapping("/product")
public class ProductController {
    
    @Autowired
    private MeijiaService meijiaService;
    
    @Autowired
    private CateService cateService;
    
    @RequestMapping("/detail")
    public String getProductDetail(@RequestParam("id") String meijiaid, 
                                  Model model) {
        // 查询商品基本信息
        Meijia meijia = meijiaService.getMeijiaById(meijiaid);
        if (meijia == null) {
            return "error/404";
        }
        
        // 更新商品点击量
        meijiaService.updateHits(meijiaid);
        
        // 查询分类信息
        Cate cate = cateService.getCateById(meijia.getCateid());
        
        // 组装数据模型
        model.addAttribute("meijia", meijia);
        model.addAttribute("cate", cate);
        model.addAttribute("relatedProducts", 
                          meijiaService.getRelatedProducts(meijia.getCateid(), meijiaid));
        
        return "product/detail";
    }
}

对应的MyBatis映射文件实现了复杂的查询逻辑:

<!-- MeijiaMapper.xml -->
<mapper namespace="com.mapper.MeijiaMapper">
    
    <resultMap id="MeijiaResultMap" type="com.entity.Meijia">
        <id property="meijiaid" column="meijiaid"/>
        <result property="meijianame" column="meijianame"/>
        <result property="image" column="image"/>
        <result property="cateid" column="cateid"/>
        <result property="price" column="price"/>
        <result property="recommend" column="recommend"/>
        <result property="thestart" column="thestart"/>
        <result property="theend" column="theend"/>
        <result property="hits" column="hits"/>
        <result property="sellnum" column="sellnum"/>
        <result property="contents" column="contents"/>
    </resultMap>
    
    <select id="getMeijiaById" parameterType="String" resultMap="MeijiaResultMap">
        SELECT * FROM meijia WHERE meijiaid = #{meijiaid}
    </select>
    
    <update id="updateHits" parameterType="String">
        UPDATE meijia SET hits = hits + 1 WHERE meijiaid = #{meijiaid}
    </update>
    
    <select id="getRelatedProducts" resultMap="MeijiaResultMap">
        SELECT * FROM meijia 
        WHERE cateid = #{cateid} AND meijiaid != #{excludeId} 
        ORDER BY sellnum DESC LIMIT 6
    </select>
</mapper>

商品详情页面

购物车与订单生成流程

购物车功能采用Session存储临时数据,订单生成时进行库存校验和事务处理:

@Service
@Transactional
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private MeijiaMapper meijiaMapper;
    
    @Autowired
    private CartService cartService;
    
    public String createOrder(Order order, List<CartItem> cartItems) {
        // 校验库存
        for (CartItem item : cartItems) {
            Meijia meijia = meijiaMapper.getMeijiaById(item.getMeijiaid());
            if (meijia.getStock() < item.getQuantity()) {
                throw new InventoryException("商品库存不足: " + meijia.getMeijianame());
            }
        }
        
        // 生成订单号
        String orderid = "ORD" + System.currentTimeMillis();
        order.setOrderid(orderid);
        order.setStatus("待付款");
        order.setAddtime(VeDate.getNow());
        
        // 保存订单主信息
        orderMapper.insertOrder(order);
        
        // 保存订单明细并扣减库存
        for (CartItem item : cartItems) {
            OrderDetail detail = new OrderDetail();
            detail.setDetailid("DET" + VeDate.getStringId());
            detail.setOrderid(orderid);
            detail.setMeijiaid(item.getMeijiaid());
            detail.setQuantity(item.getQuantity());
            detail.setPrice(item.getPrice());
            
            orderMapper.insertOrderDetail(detail);
            meijiaMapper.updateStock(item.getMeijiaid(), item.getQuantity());
            meijiaMapper.updateSellnum(item.getMeijiaid(), item.getQuantity());
        }
        
        // 清空购物车
        cartService.clearCart(order.getUserid());
        
        return orderid;
    }
}

订单提交页面

管理员商品管理功能

后台管理系统提供完整的商品CRUD操作,支持批量上下架和推荐位管理:

@Controller
@RequestMapping("/admin")
public class AdminMeijiaController {
    
    @Autowired
    private MeijiaService meijiaService;
    
    @RequestMapping("/meijia/list")
    public String meijiaList(@RequestParam(defaultValue = "1") int page, 
                            @RequestParam(defaultValue = "10") int size,
                            Model model) {
        PageHelper.startPage(page, size);
        List<Meijia> meijiaList = meijiaService.getAllMeijia();
        PageInfo<Meijia> pageInfo = new PageInfo<>(meijiaList);
        
        model.addAttribute("pageInfo", pageInfo);
        model.addAttribute("meijiaList", meijiaList);
        return "admin/meijia/list";
    }
    
    @PostMapping("/meijia/save")
    @ResponseBody
    public Map<String, Object> saveMeijia(Meijia meijia, 
                                         @RequestParam("imageFile") MultipartFile imageFile) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 处理图片上传
            if (!imageFile.isEmpty()) {
                String filename = saveUploadImage(imageFile);
                meijia.setImage(filename);
            }
            
            // 设置默认值
            if (meijia.getMeijiaid() == null) {
                meijia.setMeijiaid("M" + VeDate.getStringId());
                meijia.setHits("0");
                meijia.setSellnum("0");
                meijiaService.insertMeijia(meijia);
            } else {
                meijiaService.updateMeijia(meijia);
            }
            
            result.put("success", true);
            result.put("message", "保存成功");
        } catch (Exception e) {
            result.put("success", false);
            result.put("message", "保存失败: " + e.getMessage());
        }
        
        return result;
    }
    
    private String saveUploadImage(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        String filename = UUID.randomUUID().toString() + extension;
        
        File dest = new File("/upload/images/" + filename);
        file.transferTo(dest);
        
        return filename;
    }
}

商品管理界面

用户认证与权限控制

系统采用基于Session的认证机制,通过拦截器实现权限验证:

@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        String uri = request.getRequestURI();
        
        // 公开路径放行
        if (uri.contains("/login") || uri.contains("/register") || 
            uri.contains("/product") || uri.contains("/static")) {
            return true;
        }
        
        // 管理员路径验证
        if (uri.contains("/admin")) {
            Admin admin = (Admin) request.getSession().getAttribute("admin");
            if (admin == null) {
                response.sendRedirect(request.getContextPath() + "/admin/login.jsp");
                return false;
            }
        }
        
        // 用户路径验证
        if (uri.contains("/user")) {
            Users user = (Users) request.getSession().getAttribute("user");
            if (user == null) {
                response.sendRedirect(request.getContextPath() + "/login.jsp");
                return false;
            }
        }
        
        return true;
    }
}

对应的Spring配置:

<!-- spring-mvc.xml -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.interceptor.AuthInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

用户登录界面

实体模型设计精要

系统实体类采用JavaBean规范设计,通过工具类自动生成主键:

package com.entity;

import com.util.VeDate;

public class Meijia {
    private String meijiaid = "M" + VeDate.getStringId();
    private String meijianame;
    private String image;
    private String cateid;
    private String price;
    private String recommend;
    private String thestart;
    private String theend;
    private String hits;
    private String sellnum;
    private String contents;
    
    // Getter和Setter方法
    public String getMeijiaid() { return meijiaid; }
    public void setMeijiaid(String meijiaid) { this.meijiaid = meijiaid; }
    
    public String getMeijianame() { return meijianame; }
    public void setMeijianame(String meijianame) { this.meijianame = meijianame; }
    
    // 其他getter/setter方法...
    
    /**
     * 计算折扣价格
     */
    public String getDiscountPrice() {
        if ("是".equals(recommend)) {
            double originalPrice = Double.parseDouble(price);
            double discountPrice = originalPrice * 0.9; // 推荐商品9折
            return String.format("%.2f", discountPrice);
        }
        return price;
    }
}

工具类VeDate提供ID生成和时间处理功能:

package com.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

public class VeDate {
    
    public static String getStringId() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String timestamp = sdf.format(new Date());
        Random random = new Random();
        int randomNum = random.nextInt(1000);
        return timestamp + String.format("%03d", randomNum);
    }
    
    public static String getNow() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(new Date());
    }
}

系统性能优化策略

数据库查询优化

通过MyBatis的分页插件实现物理分页,避免大数据量查询的内存溢出:

<!-- mybatis-config.xml -->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="helperDialect" value="mysql"/>
        <property name="reasonable" value="true"/>
        <property name="supportMethodsArguments" value="true"/>
    </plugin>
</plugins>

静态资源缓存配置

在Spring MVC中配置静态资源缓存策略,提升页面加载速度:

<mvc:resources mapping="/static/**" location="/static/" 
               cache-period="2592000"/>

事务管理优化

使用Spring声明式事务管理,针对不同业务场景配置事务隔离级别:

@Service
public class OrderService {
    
    @Transactional(isolation = Isolation.READ_COMMITTED, 
                   propagation = Propagation.REQUIRED,
                   rollbackFor = Exception.class)
    public String createOrder(Order order, List<CartItem> cartItems) {
        // 订单创建逻辑
    }
}

功能扩展与架构演进规划

分布式缓存集成

引入Redis作为二级缓存,减轻数据库压力:

@Service
public class MeijiaServiceWithCache {
    
    @Autowired
    private RedisTemplate<String, Meijia> redisTemplate;
    
    @Autowired
    private MeijiaMapper meijiaMapper;
    
    public Meijia getMeijiaById(String meijiaid) {
        String cacheKey = "meijia:" + meijiaid;
        Meijia meijia = redisTemplate.opsForValue().get(cacheKey);
        
        if (meijia == null) {
            meijia = meijiaMapper.getMeijiaById(meijiaid);
            if (meijia != null) {
                redisTemplate.opsForValue().set(cacheKey, meijia, 30, TimeUnit.MINUTES);
            }
        }
        
        return meijia;
    }
}

微服务架构改造

本文关键词
SSM框架在线美甲商城源码解析数据库设计B2C电商平台

上下篇

上一篇
没有更多文章
下一篇
没有更多文章