# shop **Repository Path**: paphae/shop ## Basic Information - **Project Name**: shop - **Description**: test - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2020-05-18 - **Last Updated**: 2024-12-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README --- title: javaWeb项目-仿小米商城 date: 2020-05-15 22:49:09 tags: 项目 categories: javaWeb --- ## 使用说明 本项目导入idea后,需要配置maven,添加Web框架,修改db.properties数据库信息。 运行环境: idea2019.3 mysql8.0 tomcat9.0.24 谷歌浏览器 ## 需求分析  ## 数据库设计 (1)用户表user的详细设计如表3-1所示: 表3-1 用户表 | No. | 字段名 | 数据类型 | Def. | Pk | Fk | 备注 | | ---- | ------ | -------- | ---- | ---- | ---- | -------- | | 1 | uid | Int | | Y | | 用户id | | 2 | uname | varchar | | | | 用户名 | | 3 | pwd | varchar | | | | 密码 | | 4 | name | varchar | | | | 真实姓名 | | 5 | email | varchar | | | | 邮箱 | | 6 | sex | varchar | | | | 性别 | | 7 | level | varchar | | | | 登录 | | 8 | uimage | varchar | | | | 头像 | | 9 | birth | date | | | | 生日 | (2)商品表product的详细设计如表3-2所示: 表3-2 商品表 | No. | 字段名 | 数据类型 | Def. | Pk | Fk | 备注 | | ---- | ------------ | -------- | ---- | ---- | ---- | ---------- | | 1 | pid | int | | Y | | 商品id | | 2 | pname | varchar | | | | 商品名称 | | 3 | market_price | double | | | | 原价 | | 4 | shop_price | double | | | | 销售价格 | | 5 | pnumb | int | | | | 库存 | | 6 | pimage | varchar | | | | 商品图片 | | 7 | pdate | date | | | | 上架日期 | | 8 | is_hot | int | | | | 是否是热卖 | | 9 | pdesc | varchar | | | | 商品描述 | | 10 | pflag | int | | | | 上架下架 | | 11 | cid | varchar | | | | 类型id | (3)类型表category的详细设计如表3-3所示: 表3-3 类型表 | No. | 字段名 | 数据类型 | Def. | Pk | Fk | 备注 | | ---- | ------ | -------- | ---- | ---- | ---- | ------------------------------- | | 1 | cid | varchar | | Y | | 类型id 和商品表cid 逻辑外键关系 | | 2 | cname | varchar | | | | 类型名称 | (4)地址表address的详细设计如表3-4所示: 表3-4 地址表 | No. | 字段名 | 数据类型 | Def. | Pk | Fk | 备注 | | ---- | ----------- | -------- | ---- | ---- | ---- | ------------------- | | 1 | aid | int | | Y | | 地址id | | 2 | callname | varchar | | | | 联系人姓名 | | 3 | callphone | varchar | | | | 联系人手机号 | | 4 | calladdress | varchar | | | | 联系人地址 | | 5 | uid | int | | | | 与用户表中的uid一致 | (5)订单表order的详细设计如表3-5所示: 表3-5 订单表 | No. | 字段名 | 数据类型 | Def. | Pk | Fk | 备注 | | ---- | ------ | -------- | ---- | ---- | ---- | ------------------- | | 1 | oid | varchar | | Y | | 订单id | | 2 | odate | datetime | | | | 提交时间 | | 3 | ostatu | varchar | | | | 订单状态 | | 4 | total | double | | | | 总价 | | 5 | uid | int | | | | 用户表uid为外键关系 | | 6 | aid | int | | | | 逻辑外键地址id | (6)订单条目表orderitem的详细设计如表3-6所示: 表3-6 订单条目表 | No. | 字段名 | 数据类型 | Def. | Pk | Fk | 备注 | | ---- | ------ | -------- | ---- | ---- | ---- | -------- | | 1 | tid | int | | Y | | 条目id | | 2 | pid | int | | | | 商品id | | 3 | numb | int | | | | 购买数量 | | 4 | sum | double | | | | 小计 | | 5 | oid | varchar | | | | 订单id | # 商品信息模块 ## 商品一级菜单 在加载页面时,通过ajax与数据库交互,获得商品类别信息,并使用拼接html的方式将含有商品类别信息的html代码植入到一级菜单类别的父div中。 ```javascript $(function () { $.post("${pageContext.request.contextPath}/selectAllCategory.do",function (data) { var str = "
#### **实现效果**:当鼠标悬浮在一级菜单上时,显示该分类的所有商品信息
#### 实现方式(两种):
##### 第一种:实时查询
当鼠标悬浮在一级菜单上的某个类别时,触发ajax的鼠标悬浮事件,完成与数据库的交互。代码如下:
```javascript
$(function () {
$(document).on("mouseover",".category-hover",function(e){
var cid = $(this).attr("name");
var $pop = $($(this).children("div").get(0));
var $div1 = $($pop.children("div").get(0));
var $div2 = $($pop.children("div").get(1));
$.post("${pageContext.request.contextPath}/selectProductByCid.do",{cid:cid},function (data) {
console.log(data);
var str1 = "",str2 = "";
for (var i = 0 ; i
在商品列表页面中点击某个商品,会通过a链接跳转到查询商品信息的servlet,a标签的地址拼接了商品id,在servlet中通过与数据库交互,查询出改商品id的详细信息。将商品详情信息存到request中,并转发到商品详情页面。java代码如下:
```java
String pid = request.getParameter("pid");
Product product = productService.findbyid(Integer.parseInt(pid));
Cookie[] cookies = request.getCookies();
boolean flag =false;
if(cookies!=null&&cookies.length>0){
for (Cookie cookie : cookies) {
String name = cookie.getName();
if("history".equals(name)){
flag = true;
String value = cookie.getValue();
value = URLDecoder.decode(value, "utf-8");
List
使用JSTL和EL表达式在商品详情页面中显示商品信息,html代码如下:
```html
用js实现的,设置了一个index索引表示当前轮播图片的索引,和一个滑过清除定时器,即设置一个计时器,比如设置3秒后显示下一张轮播图片,那么就会通过index++操作来进行翻页,显示图片单独定义一个方法,根据当前索引index来显示对应的图片,通过修改css样式的display属性为block,其他的图片设置为none,达到显示图片的效果。
当鼠标滑过轮播图片的时候,就会清除这个计时器,这时图片是一直停止在当前图片上面的,当鼠标离开轮播图片时,重新计时。
可以手动点击上一张,下一张来实现翻页,实现原理是,通过修改index的值,然后进行图片的显示。
js代码如下:
```javascript
//封装一个代替getElementById()的方法
function byId(id){
return typeof(id) === "string" ? document.getElementById(id) : id;
}
//全局变量
var index = 0;
var timer = null;
var pics = byId("banner").getElementsByTagName("div");
var dots = byId("dots").getElementsByTagName("span");
var prev = byId("prev");
var next = byId("next");
var len = pics.length;
var menu = byId("menu-content");
var menuItems = menu.getElementsByClassName("menu-item");
var subMenu = byId("sub-menu"),
innerBox = subMenu.getElementsByClassName("inner-box");
console.log(len);
function slideImg(){
var main = byId("main");
//滑过清除定时器,离开继续
main.onmouseover = function(){
//滑过清楚定时器
if (timer) {
clearInterval(timer);
}
}
main.onmouseout = function(){
timer = setInterval(function(){
index++;
if (index >= len) {
index = 0;
}
//切换图片
console.log(index);
changeImg();
},3000);
}
// 自动在main上触发鼠标离开的事件
main.onmouseout();
//问题:事件相当于一个函数,可以直接调用?
//
for(var d = 0 ; d < len ; d++){
//function中d为最终值,所以不能用d来表示index的值
//给所有span添加一个id的属性,值为d,作为当前span的索引
dots[d].id = d;
dots[d].onclick = function(){
//改变index为当前span的索引
index = this.id;
//调用changeImg,切换图片
changeImg();
}
}
//下一张
next.onclick = function(){
index++;
if (index >= len) {
index = 0;
}
changeImg();
}
//上一张
prev.onclick = function(){
index--;
if (index < 0) {
index = len - 1;
}
changeImg();
}
//导航菜单
//遍历主菜单,且绑定事件
for(var m = 0 ; m < menuItems.length ; m++){
//非HTML关键字,如id 自定义属性要使用 setAttribute和getAttribute;
menuItems[m].setAttribute("data-index",m);
menuItems[m].onmouseover = function(){
//鼠标未划过主菜单时,隐藏所有子菜单
for(var j = 0 ; j < innerBox.length ; j++){
innerBox[j].style.display = 'none';
menuItems[j].style.background = 'none';
}
//为每一个menu-item定义data-index属性,作为索引
var idx = this.getAttribute("data-index");
subMenu.className = 'sub-menu';
innerBox[idx].style.display = 'block';
menuItems[idx].style.background = 'rgba(0,0,0,0.1)';
}
}
menu.onmouseout = function(){
subMenu.className = "sub-menu hide";
}
subMenu.onmouseover = function(){
subMenu.className = "sub-menu";
}
subMenu.onmouseout = function(){
subMenu.className = "sub-menu hide";
}
}
//切换图片
function changeImg(){
//遍历所有的图片并隐藏
for(var i = 0 ; i < len ; i++){
pics[i].style.display = "none";
dots[i].className = "";
}
//显示当前index对应的图片
pics[index].style.display = "block";
dots[index].className = "active";
}
slideImg();
```
## 验证码功能
java工具类代码如下:
```java
public class CaptcahCode {
/**
* 验证生成的方法
* @param response
* @return
*/
public static String drawImage(HttpServletResponse response){
//1:定义以字符串的拼接的StringBuilder
StringBuilder builder = new StringBuilder();
//准备产生4个字符串的随机数
for(int i=0;i<4;i++){
builder.append(randomChar());
}
String code = builder.toString();
//2:定义图片的宽度和高度
int width = 70;
int height = 25;
//简历bufferedImage对象,制定图片的长度和宽度以及色彩
BufferedImage bi = new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR);
//3:获取到 Graphics2D 绘制对象,开始绘制验证码
Graphics2D g = bi.createGraphics();
//4:设置文字的字体和大小
Font font = new Font("微软雅黑",Font.PLAIN,20);
//设置字体的颜色
Color color = new Color(0,0,0);
//设置字体
g.setFont(font);
//设置颜色
g.setColor(color);
//设置背景
g.setBackground(new Color(226,226,240));
//开始绘制对象
g.clearRect(0,0,width,height);
//绘制形状,获取矩形对象
FontRenderContext context = g.getFontRenderContext();
Rectangle2D bounds = font.getStringBounds(code,context);
//计算文件的坐标和间距
double x = (width - bounds.getWidth())/2;
double y = (height - bounds.getHeight())/2;
double ascent = bounds.getY();
double baseY = y - ascent;
g.drawString(code,(int)x,(int)baseY);
//结束绘制
g.dispose();
try {
ImageIO.write(bi,"jpg",response.getOutputStream());
//刷新响应流
response.flushBuffer();
}catch(Exception ex){
ex.printStackTrace();
}
return code;
}
/**
* 算术表达式验证码
*
* 1:干扰线的产生
* 2: 范围随机颜色,随机数
*
* @param response
* @return
*/
public static String drawImageVerificate(HttpServletResponse response){
//定义验证码的宽度和高度
int width = 100,height = 30;
//在内存中创建图片
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//创建图片的上下文
Graphics2D g = image.createGraphics();
//产生随机对象,此随机对象主要用于算术表达式的数字
Random random = new Random();
//设置背景
g.setColor(getRandomColor(240,250));
//设置字体
g.setFont(new Font("微软雅黑", Font.PLAIN,22));
//开始绘制
g.fillRect(0,0,width,height);
//干扰线的绘制,绘制线条到图片中
g.setColor(getRandomColor(180,230));
for(int i=0;i<10;i++){
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(60);
int y1 = random.nextInt(60);
g.drawLine(x,y,x1,y1);
}
//开始进行对算术验证码表达式的拼接
int num1 = (int)(Math.random()*10 + 1);
int num2 = (int)(Math.random()*10 + 1);
int fuhao = random.nextInt(3);//产生一个[0,2]之间的随机整数
//记录符号
String fuhaostr = null;
int result = 0;
switch (fuhao){
case 0 : fuhaostr = "+";result = num1 + num2;break;
case 1: fuhaostr = "-";result = num1 - num2;break;
case 2 : fuhaostr = "*";result = num1 * num2;break;
//case 3 : fuhaostr = "/";result = num1 / num2;break;
}
//拼接算术表达式,用户图片显示。
String calc = num1 + " " + fuhaostr +" "+ num2 +" = ?";
//设置随机颜色
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
//绘制表达式
g.drawString(calc,5,25);
//结束绘制
g.dispose();
try {
//输出图片到页面
ImageIO.write(image,"JPEG",response.getOutputStream());
return String.valueOf(result);
}catch (Exception ex){
ex.printStackTrace();
return null;
}
}
/**
* 范围随机颜色
* @param fc
* @param bc
* @return
*/
public static Color getRandomColor(int fc,int bc){
//利用随机数
Random random = new Random();
//随机颜色,了解颜色-Color(red,green,blue).rgb三元色 0-255
if(fc>255)fc = 255;
if(bc>255)bc = 255;
int r = fc+random.nextInt(bc-fc);
int g = fc+random.nextInt(bc-fc);
int b = fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
/**
* 此方法用户产生随机数字母和数字
* @return
*/
private static char randomChar(){
//1:定义验证需要的字母和数字
String string = "QWERTYUIOPASDFGHJKLZXCVBNM0123456789";
//2:定义随机对象
Random random = new Random();
return string.charAt(random.nextInt(string.length()));
}
}
```
jsp页面代码如下:
```jsp
<%@ page import="com.zhongruan.util.CaptcahCode" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
//1:清空浏览器缓存,目的是为了清空浏览器的缓存,因为浏览器
//会对网站的资源文件和图像进行记忆存储,如果被浏览器加载过的图片就记忆起来,记忆以后
//文件就不会和服务器在交互,如果我们验证不清空的话可能会造成一个问题就是:验证刷新以后没有效果。
response.setHeader("pragma","no-cache");
response.setHeader("cache-control","no-cache");
response.setHeader("expires","0");
//2:调用编写的生成验证码的工具
String code = CaptcahCode.drawImage(response);
session.setAttribute("code",code);
//3:如何解决getOutputStream异常问题
out.clear();
out = pageContext.pushBody();
%>
```
用法:
```html