校园O2O项目笔记
1.项目能解决什么问题:随着互联网的快速发展,校园中的学生购买小成本商品的趋势已经逐年降低!本项目针对的是校园店铺o2o项目,用来解决局部区域的店铺信息共享的痛点,即某个区域内中小店铺迫切想宣传自己店铺及商品,同时该区域的或者进入该区域内的顾客很难通过美团等大平台获取到该区域里的中小店铺(特别是衣服等小型店铺的信息),本项目主要是想创建一个多店铺的管理系统,系统分为前端展示和后端管理两个模块。该项目为校园中店家提供线上商品信息展示平台,用户可在后端进行店铺创建,商品创建和类别设置。
2.项目赢利点:店家使用系统,头条展示付费,店铺类别里店铺的排名,越靠前越贵,店铺优先级付费,还有网站联合商家搞线下拉活动赞助。
3.项目1.0中用到的技术栈:后端:采用SSM为项目基础框架,采用mysql数据库(主从库读写分离)存储数据,redis做缓存。
4.项目由以下几个模块组成:
1.前端展示系统(客户使用)
类型于美团,淘宝等网站,给客户提供区域信息,店铺商品展示信息,头条信息展示(网站盈利点之一,头条中的店铺信息展示可以收费),客户可以查询店铺,查看店家信息商品,找出心仪的商品进行积分。
2.店家管理系统
维护店铺信息,维护商品信息,账号管理。
3.超级管理员系统
头条信息维护,区域信息维护,权限管理,用户账号管理,店家管理。
前端展示系统:
店家系统:
超级管理员系统:
实体类关系图分析:
共需要创建这样10个实体类。
区域表应该需要包含区域ID:AreaId,区域名字AreaName,权重(用来排序,权重越大排越前面)priority,创建时间createTime,最后修改时间lastEditTime.
商品类别表应该包含:商品类别ID:ProductCategoryId,商品类别名字:ProductCategoryName,权重 priority,创建时间,修改时间,店铺ID:ShopId(与商品类别形成主外键,店铺为主键,商品类别为外键)。
商品表应该包含:商品ID:ProductId,商品名称:ProductName,商品详情信息:ProductInfo,商品图片:ProductImg,
创建时间,修改时间,商品原价price,商品现价:newPrice,商品积分point,权重,商品状态state(0,上架,1下架),,商品类别ID:ProductCategoryId(与店铺类别形成主外键,一个商品类别有多个商品),店铺Id:shopId(与店铺形成主外键,一个店铺会有多个商品)。
详情图片表:详情图片ID,详情图片地址,图片描述,权重,创建时间,商品Id(与商品图片形成主外键)
店铺表:包含:店铺ID:shopId,店铺名字:ShopName,权重,创建时间,店铺描述,联系方式,图片,状态,建议(超级管理员对店铺的建议),店铺类别ID(与店铺类别表形成主外键,店铺类别表为主键),用户Id(与用户形成主外键,用户为主键,一共用户会创建多个店铺)
店铺类别表:包含店铺类别Id,店铺类别名称ShopName,权重,上级ID,parentId。。。。
用户信息表包含用户ID,用户的姓名:PersonName,性别,年龄,头像,邮箱,状态(判断用户是否可用),身份标识(1,为顾客,2.为商家3.为超级管理员),创建时间,修改时间。
本地账号:本地账号ID,用户名,密码,创建时间,用户Id(与用户Id起主外键作用,用户信息为主键)
微信账号类,微信账号Id,openId(微信号与公众号绑定的唯一标识),创建时间,用户Id(与用户Id起主外键)
假设用户小王要买一只钢笔,钢笔是A店铺里面的商品,这时相当于A店铺里增加了一个订单数据,怎么设计你的数据库。
由于钢笔只是A店铺内的其中一件商品,而店铺内肯定还有其他商品,所以商品表中需要有店铺ID作为主外键,店铺Id作为主键,SOL语句可以是
INSERT INTO tb_product(product_name,product_desc,img_addr, normal_price,promotion_price,priority,create_time, last_edit_time,enable_status,point,product_category_id, shop_id) VALUES (#{productName},#{productDesc},#{imgAddr}, #{normalPrice},#{promotionPrice},#{priority},#{createTime}, #{lastEditTime},#{enableStatus},#{point},#{productCategory.productCategoryId}, #{shop.shopId})
这里的ProductCategory是从前端由用户选择获取的,而这里的shop是由Session域里获取的,因为是进入某个店铺里面增加商品,所以可以从session域里获取这个当前的shop。这样就把商品,店铺以及商品类别联系在一起了。
4.店铺注册模块流程图(增)![店铺注册功能模块]
注:在查询店铺类别的时候,ShopCategoryDao.xml,当需要进行判断时用if语句
<if test="shopCategoryCondition==null"> and parent_id is null </if> <if test="shopCategoryCondition!=null"> and parent_id is not null </if> <if test="shopCategoryCondition!=null and shopCategoryCondition.parent!=null"> and parent_id=#{shopCategoryCondition.parent.shopCategoryId} </if>
店铺编辑和列表模块(改)
要实现该模块需要两个步骤:1.实现单个店铺信息的获取2.对店铺进行编辑修改。
首先更新店铺语句采用动态sql语句,具体如下:
<update id="updateShop" parameterType="com.imooc.o2o.entity.Shop"> update tb_shop <set> <if test="shopName != null">shop_name=#{shopName},</if> <if test="shopDesc != null">shop_desc=#{shopDesc},</if> <if test="shopAddr != null">shop_addr=#{shopAddr},</if> <if test="phone != null">phone=#{phone},</if> <if test="shopImg != null">shop_img=#{shopImg},</if> <if test="priority != null">priority=#{priority},</if> <if test="lastEditTime != null">last_edit_time=#{lastEditTime},</if> <if test="enableStatus != null">enable_status=#{enableStatus},</if> <if test="advice != null">advice=#{advice},</if> <if test="area != null">area_id=#{area.areaId},</if> <if test="shopCategory != null">shop_category_id=#{shopCategory.shopCategoryId}</if> </set> where shop_id=#{shopId} </update>
根据传入的shop实例中的shopId来更新相对应的店铺信息。由于我们在页面上不仅要返回Shop实体类还需要返回商铺类别中的ShopCategoryName和所属区域的AreaName,所以在ShopDao.xml中通过resultMap去接收返回值具体配置如下:
<resultMap type="com.imooc.o2o.entity.Shop" id="shopMap"> <id column="shop_id" property="shopId" /> <result column="shop_name" property="shopName" /> <result column="shop_desc" property="shopDesc" /> <result column="shop_addr" property="shopAddr" /> <result column="phone" property="phone" /> <result column="shop_img" property="shopImg" /> <result column="priority" property="priority" /> <result column="create_time" property="createTime" /> <result column="last_edit_time" property="lastEditTime" /> <result column="enable_status" property="enableStatus" /> <result column="advice" property="advice" /> #对于复合类型,用association标签,property与成员变量名字对应为area,column表示通过area_id进行连接 javaType表示连接的表为Area表,然后在Area表中获取两个元素分别为areaId和areaName。,下面的shopCategory和owner类似 <association property="area" column="area_id" javaType="com.imooc.o2o.entity.Area"> <id column="area_id" property="areaId" /> <result column="area_name" property="areaName" /> </association> <association property="shopCategory" column="shop_category_id" javaType="com.imooc.o2o.entity.ShopCategory"> <id column="shop_category_id" property="shopCategoryId" /> <result column="shop_category_name" property="shopCategoryName" /> </association> <association property="owner" column="user_id" javaType="com.imooc.o2o.entity.PersonInfo"> <id column="user_id" property="userId" /> <result column="name" property="name" /> </association> </resultMap>
由登入页面,输入账号密码登入后进入shoplist页面,在获取shoplist页面的时候,根据从前端获取的user对象即账号作为搜索条件查询该账号下的店铺列表,查询完店铺列表在前端进行显示,然后点击进入
的时候就会携带对应的shopId的参数,进入店铺管理界面,然后再点击店铺信息进入商铺详情页面<a id="shopInfo" href="/o2o/shopadmin/shopoperation"。在前端shopoperation.js中通过判断是否有携带shopid这个参数来确定是编辑还是新增店铺。所以首先是由URL获取到ShopID,所以此为编辑操作,接着通过controller层的ShopManagementController下的getShopById(HttpServletRequest request)方法,再调用service层shopServiceimpl下的getByShopId(Long shopId)和AreaServiceimpl下的getAreaList()方法。最后再Dao层的ShopDao下的queryByShopId(long shopId)方法和AreaDao中的queryArea()方法获取商铺信息和区域信息。这里我们让用户不能修改店铺的类别,给店铺类别选定原先的店铺类别值
接下来进行第二部的操作即商铺修改操作。通过controller层的ShopManagementController下的modifyShop(HttpServletRequest request)方法,通过mapper.readValue(shopStr,Shop.class)方法将request接收到的字符串转换为Shop实体类,并将这个转换好的Shop实体类调用shopService.modifyShop(shop, imageHolder)方法进入Service层进行更新。在Service层中ShopServiceImpl下的modifyShop方法,首先判断是否需要处理图片,若是需要处理图片,则先将原商铺的图片删除,然后再调用shopDao.updateShop(shop)方法。Dao层则直接调用shopDao.updateShop(shop)将编辑后的店铺存入到数据库中。
店铺查询列表模块:(查)
由登入页面,输入账号密码登入后进入shoplist页面,在获取shoplist页面的时候,根据从前端获取的user对象即账号作为搜索条件查询该账号下的店铺列表,在Controller层的ShopManagementController下的getShopList(HttpServletRequest request)方法,首先先从域中取出user对象作为Service层中shopService.getShopList(shopCondition, 0, 100)中的shopCondition条件,就是指哪个用户所创建的商铺列表。列出店铺成功之后,将店铺放入session域中作为权限验证依据,即该账号只能操作它创建的店铺。在Service层中ShopServiceImpl的getShopList(Shop shopCondition, int pageIndex, int pageSize)方法,最后调用Dao层的List<shop> shopList = shopDao.queryShopList(shopCondition, rowIndex, pageSize)方法。最后从数据库中查到的数据返回页面。</shop>
在进行其他如查询商铺类别下的商铺列表:shopdao.xml编写如下:
<select id="queryShopList" resultMap="shopMap"> SELECT s.shop_id, s.shop_name, s.shop_desc, s.shop_addr, s.phone, s.shop_img, s.priority, s.create_time, s.last_edit_time, s.enable_status, s.advice, a.area_id, a.area_name, sc.shop_category_id, sc.shop_category_name FROM tb_shop s, tb_area a, tb_shop_category sc <where> #如果店铺类别不为空且店铺的ID不为空,且前排的parentid为空,就是查询主目录 <if test="shopCondition.shopCategory != null and shopCondition.shopCategory.shopCategoryId != null"> and s.shop_category_id = #{shopCondition.shopCategory.shopCategoryId} </if> #查询店铺的子目录 <if test="shopCondition.shopCategory != null and shopCondition.shopCategory.parent!=null and shopCondition.shopCategory.parent.shopCategoryId !=null"> and s.shop_category_id in (select shop_category_id from tb_shop_category WHERE parent_id = #{shopCondition.shopCategory.parent.shopCategoryId}) </if> #查询所在区域的店铺 <if test="shopCondition.area != null and shopCondition.area.areaId != null"> and s.area_id = #{shopCondition.area.areaId} </if> #通过店铺名模糊查询 <if test="shopCondition.shopName != null"> and s.shop_name like '%${shopCondition.shopName}%' </if> #通过店铺状态查询 <if test="shopCondition.enableStatus != null"> and s.enable_status = #{shopCondition.enableStatus} </if> <if #通过用户查询店铺列表 test="shopCondition.owner != null and shopCondition.owner.userId != null"> and s.owner_id = #{shopCondition.owner.userId} </if> and s.area_id=a.area_id and s.shop_category_id=sc.shop_category_id </where> order by s.priority desc LIMIT #{rowIndex},#{pageSize} </select>
分页查询
public class PageCalculator { public static int calculateRowIndex(int pageIndex,int pageSize){ return (pageIndex>0)?(pageIndex-1)*pageSize:0; } }
商品类别删除模块(删)
首先从Session域中获取当前店铺,和前端传过来的参数productCategoryId参数作为productCategoryService.deleteProductCategory(productCategoryId,
currentShop.getShopId());删除的条件参数,首先在service层中先将此商品类别下的商铺的类别ID置为空,因为由于与商品存在主外键的关系,不能直接删除存在商品的该商品类别。调用productDao.updateProductCategoryToNull(productCategoryId)方法置空。然后调用productCategoryDao.deleteProductCategory(productCategoryId, shopId)将当前店铺的商品类别删除:ProductCategoryDao.xml文件上的删除语句:
<delete id="deleteProductCategory"> delete from tb_product_category where product_category_id=#{productCategoryId} AND shop_id=#{shopId} </delete>
项目中遇到的问题,发现项目部署到阿里云服务器后,对商品的编辑操作会失败,显示提交失败,而在本地可以正常提交,解决思路,用阿里云服务器和本地IDEA进行远程调试,经过一步步调试发现,发现删除已存储图片和插入图片都没问题,路径也没问题,错误原因出现在给图片增加水印,无法给更新的添加水印,异常显示为(权限不够),此时发现水印位置也是正确的,所以此时想到我部署的项目在普通账户中,没有权限执行写入执行的权限,为了验证,我将项目部署到root账户运行后成功编辑了商品,问题找到并解决了。