suitdiy男装定制网 分析、设计、实现

摘要

本网站为男装定制网,实现西装在线定制的功能。本网站将用户分为匿名用户、注册客户、生产部门、配送部门、管理部门、系统管理员6个角色,不同的角色有不同的功能和职责。本网站用javaee实现,具体采用了spring、struts、hibernate、spring security等框架,数据库使用了mysql。本网站还整合了一个discuz论坛。

一、概述

时间:本项目由个人独立完成。从需求分析到编码实现、完成各种文档,历时约3个月(主要在寒假内完成)。

粗略统计程序规模(仅包括主站中自己写的代码):java代码(*.java):7743行;jquery代码(*.js):2473行;css(*.css):5165行;freemarker模板文件(*.ftl):7220行

二、需求分析

本系统的权限分为6个:

1.匿名用户

① 可以注册、登录;

② 可以搜索、浏览产品,可以个性化定制产品;

③ 可以使用购物车存放产品;

④ 可以修改购物车中产品的数量、定制信息、尺寸信息等;

⑤ 可以浏览网站的其他各种信息;

⑥ 可以使用在线交流功能;

⑦ 可以浏览定制论坛中的各种信息。

2.注册客户

① 拥有匿名用户的全部功能;

② 可以下订单,可以查看账户的历史订单信息;

③ 可以设置账户的体型信息和各种尺寸信息(在账户中设置了体型信息和各种尺寸信息后,在登录的状态下定制产品时,录入尺寸页面和录入体型信息页面会自动加载本账户中保存的信息);

④ 可以设置账户的基本资料、联系方式;

⑤ 可以修改密码;

⑥ 可以添加、删除、编辑配送地址信息(账户中设置了一个或多个配送地址信息后,在创建订单页面会自动加载这些地址信息,可以选择这些已有的地址,也可以重新创建新地址,重新创建的新地址将会自动保存到用户账户中)。

3.生产部门 

① 可以查看等待生产的订单的详细信息(订单号、订单状态、产品的名称、数量、产品的个性化设置信息、尺寸信息、体型信息);

② 可以对等待生产的订单点击开始生产;

③ 可以查看正在生产中的订单的详细信息;

④ 可以查看生产完毕的订单的详细信息。

4.配送部门

① 可以查看等待配送的订单的详细信息(订单号、订单状态、产品的名称、数量、订单的配送地址);

② 可以对等待配送的订单点击发货;

③ 可以查看已经配送的订单的详细信息。

5.管理部门 

① 可以对网站的定制项目和定制系列进行编辑、添加、删除、上移、下移等操作(网站的导航栏中“定制目录”的下拉菜单就是从数据库中取到这些信息,并按照它们的顺序显示出来的);

② 可以添加、编辑、删除产品。(产品的信息包括产品名称、编号、价格、产品所属的定制项目和定制系列、本品包含、尺寸设置、产品列表页面的展示图、产品详细信息页面的背景图、产品描述等)。

6.系统管理员

① 可以查看注册客户的详细信息;

② 可以查看业务日志信息(哪个用户,在什么时间,什么地点(IP地址),做了什么事情);

③ 可以查看各部门人员的详细信息;

④ 可以编辑各部门人员的详细信息;

⑤ 可以添加、删除部门人员用户;

三、整体设计和功能

1、系统目标:

① 良好的客户体验

② 数据的完整性、正确性

③ 系统的稳定性、安全性

④ 系统的可扩展、可维护性

2、开发环境:

服务器端:

操作系统:windows

Web服务器:tomcat6.0、httpd2.2

Java开发包:jdk1.6

php5.3

Discuz7.2

Jms provider:activeMQ5.4

Javamail1.4

视图层:freemarker2.3.16

数据库:mysql 5.1.45

浏览器端:

js:jQuery1.4.2

Css:css2

浏览器:IE7及其以上、firefox、chrome等。

分辨率:1024*768

Web开发框架:spring3.04 + struts2.1.8 + hibernate3.5.1 + spring serurity3.1.0

主要的开发工具:Eclipse、Photoshop、Firebug

3、系统物理部署图:

① 本系统用javaEE开发,使用了spring、struts、hibernate框架;

② 使用spring security进行权限管理;

③ 视图层使用了freemarker模板技术;

④ 使用javamail发送邮件,当用户注册、创建订单、订单开始生产、订单生产完毕、订单已成功发货的时候,系统会自动给客户发送邮件,由于发送邮件是一件耗时的动作,所以采用异步的方式,当要发送邮件时,只给jms服务器发送一条请求发送邮件的消息,然后立即返回,提高了交互速度,发送邮件的任务由jms服务器以异步的方式完成。

⑤ 本系统整合了一个开源的论坛系统discuz7.2,这个论坛是用php做的,因此要整合tomcat服务器、apache服务器和php解释器;所有的请求都先交给apache服务器,然后静态页面、静态资源(css、js、图片等)由apache本身处理,php页面调用php解释器处理,jsp、servlet、action则交给tomcat服务器处理。

⑥ 数据库管理系统使用了mysql,分了两个库,suitdiy和suitdiy_discuz,分别是主站和discuz论坛的数据库。

 2011032314400772.png

4、系统用例图:

 2011032314410693.png

(其他省略。。。)

5、活动图

 2011032314412636.png

2011032314415966.png

(其他省略。。。)

四、数据库设计

1、概况:数据库管理系统使用了mysql。数据库中用到了存储过程、触发器、事件等。

2、概念设计:E-R图

 2011032314454322.png

3、创建触发器(trigger):

触发器是强制实施数据库中数据完整性的主要机制之一。为维护数据完整性,本系统创建了若干触发器实现保留已定义约束和应用规则的目的。在用户对指定表执行更新或删除操作时,系统自动执行相应触发器中的SQL语句,实现较复杂的完整性控制。

① item表的触发器item_update:

功能:管理人员删除item表中记录时(实际的操作是设置item_enabled字段为0),将其下关联的style记录从item中删除(实际的操作是设置style_item_enabled为0),同时判断style记录是否被完全删除(即style_item_enabled和style_serious_enabled是否全部为0),若是,则将所有购物车中对应的style记录删除。

创建:

CREATE DEFINER=`root`@`localhost` TRIGGER `item_update` AFTER UPDATE ON `item` FOR EACH ROW begin

    if new.item_enabled = 0 then

        update style set style_item_enabled = 0 where item_id = new.item_id;

        delete from product_temp where style_id in(select style_id from style where item_id = new.item_id and style_item_enabled = 0 and style_serious_enabled = 0);

    end if;

end

② serious表的触发器serious_update:

功能:管理人员删除serious表中记录时(实际的操作是设置serious_enabled字段为0),将其下关联的style记录从serious中删除(实际的操作是设置style_serious_enabled为0),同时判断style记录是否被完全删除(即style_item_enabled和style_serious_enabled是否全部为0),若是,则将所有购物车中对应的style记录删除。

创建:

CREATE DEFINER=`root`@`localhost` TRIGGER `serious_update` AFTER UPDATE ON `serious` FOR EACH ROW begin

    if new.serious_enabled = 0 then

         update style set style_serious_enabled = 0 where serious_id = new.serious_id;

         delete from product_temp where style_id in(select style_id from style where serious_id = new.serious_id and style_item_enabled = 0 and style_serious_enabled = 0);

    end if;

end

③ session_temp表的触发器delete_st:

功能:定时任务清理session_temp表中客户端浏览器的跟踪信息记录后,删除对应的product_temp表中对应的的产品信息(即购物车中的信息)。

创建:

CREATE DEFINER=`root`@`localhost` TRIGGER `delete_st` AFTER DELETE ON `session_temp` FOR EACH ROW delete from product_temp where session_id = old.id

4、创建存储过程(procedure):

存储过程可被多个程序多次调用,同时加快了系统的运行速度,提高数据安全性。

序号

名称

创建

注释

1

createSessionTemp

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`createSessionTemp`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`createSessionTemp`(IN `sessionId` varchar(255))
BEGIN
 insert into session_temp values(sessionId,now());
END $$

DELIMITER ;

添加一个客户端浏览器跟踪记录

2

createUser

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`createUser`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`createUser`(IN `id` varchar(255),IN `userName` varchar(255),IN `email` varchar(255),IN `md5Password` varchar(255),IN userRole varchar(255),IN `createdTime` datetime,IN `userEnabled` bit)
BEGIN
 insert into user(user_id,user_name,user_email,user_password,user_role,created_time,user_enabled) values(id,userName,email,md5Password,userRole,createdTime,userEnabled);
END $$

DELIMITER ;

创建一个用户

3

getEnabledStylesByItemId

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`getEnabledStylesByItemId`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`getEnabledStylesByItemId`(IN `itemId` varchar(255),IN `enabled` tinyint)
BEGIN
 select * from style where item_id = itemId and style_item_enabled = enabled;

END $$

DELIMITER ;

获取某个定制项目下所有有效的产品

4

getEnabledStylesBySeriousId

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`getEnabledStylesBySeriousId`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`getEnabledStylesBySeriousId`(IN `seriousId` varchar(255),IN `enabled` tinyint)
BEGIN
 select * from style where serious_id = seriousId and style_serious_enabled = enabled;
END $$

DELIMITER ;

获取某个定制系列下所有有效的产品

5

getLimitedLogs

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`getLimitedLogs`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`getLimitedLogs`(IN `pageSize` integer,IN `currentPage` integer)
BEGIN
 
 set @curr = (currentPage-1)*pageSize;
 set @size = pageSize;
 prepare getLogs from 'select * from business_log order by time desc limit ?,?';
 execute getLogs using @curr,@size;
 deallocate prepare getLogs;

END $$

DELIMITER ;

分页查询业务日志

6

getOrderByOrderNo

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`getOrderByOrderNo`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`getOrderByOrderNo`(IN `orderNo` varchar(255))
BEGIN
 select * from orders where order_no = orderNo;

END $$

DELIMITER ;

通过订单号查询订单

7

getUsersByRole

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`getUsersByRole`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`getUsersByRole`(IN `role` varchar(255),IN `enabled` bit)
BEGIN
 select * from user where user_role = role and user_enabled = enabled;

END $$

DELIMITER ;

查询某个角色下的所有用户

8

saveBusinessLog

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`saveBusinessLog`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`saveBusinessLog`(IN `id` varchar(255),IN `sessionId` varchar(255),IN `userId` varchar(255),IN `ip` varchar(255),IN `actionTarget` varchar(255),IN `actionType` varchar(255),IN `actionContent` varchar(255),IN `time` timestamp)
BEGIN
 insert into business_log(id,session_id,user_id,ip,action_target,action_type,action_content,time)
  values(id,sessionId,userId,ip,actionTarget,actionType,actionContent,time);

END $$

DELIMITER ;

保存业务日志

9

searchEnabledStylesByName

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`searchEnabledStylesByName`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`searchEnabledStylesByName`(IN `keywords` varchar(255))
BEGIN
  set @reg = concat('%',keywords,'%');
 prepare searchStyles from 'select * from style where style_id in
    (select style_id from (select style_id,style_name,item_name from style left outer join item
      on style.item_id = item.item_id where item_enabled = 1 and style_item_enabled = 1) style_item
      where style_item.style_name like ? or style_item.item_name like ?)
    or style_id in
    (select style_id from (select style_id,style_name,serious_name from style left outer join serious
      on style.serious_id = serious.serious_id where serious_enabled = 1 and style_serious_enabled = 1) style_serious
      where style_serious.style_name like ? or style_serious.serious_name like ?)';
  execute searchStyles using @reg,@reg,@reg,@reg;
 deallocate prepare searchStyles;

END $$

DELIMITER ;

根据用户输入的关键字搜索匹配的产品。搜索的字段包括产品名称、产品所属的系列的名称、产品所属的定制项目名称。

10

updateSessionTemp

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`updateSessionTemp`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`updateSessionTemp`(IN `sessionId` varchar(255))
BEGIN
 if (select count(id) from session_temp where id = sessionId) = 0 then
  insert into session_temp(id,last_update) values(sessionId,now());
 else
  update session_temp set last_update = now() where id = sessionId;
 end if;
END $$

DELIMITER ;

更新客户端浏览器跟踪记录的最后访问时间,如果没有对应id的记录,则插入一条记录。

5、创建事件(event,定期运行):

功能:每天零点执行一次,检查session_temp表中的记录(客户端浏览器的跟踪信息),如果记录的最后更新时间距离现在超过14天,则删除之。

创建:

CREATE EVENT `session_temp_check` ON SCHEDULE EVERY 1 DAY STARTS '2011-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO delete from session_temp where to_days(now()) - to_days(last_update) > 14

6、数据字典

(共10个表)

① business_log表

字段

数据类型

允许为空

注释

id

varchar(255)

主键

NO

日志id号

session_id

varchar(255)

 

NO

用户浏览器id号

user_id

varchar(255)

 

YES

注册用户id号

ip

varchar(255)

 

NO

ip地址

action_target

varchar(255)

 

NO

操作对象

action_type

varchar(255)

 

NO

操作类型

action_content

varchar(255)

 

NO

操作内容

time

datetime

 

NO

时间

 (其他省略。。。)

五、系统详细设计和实现

1、系统的逻辑架构设计:

整个项目采用MVC模式,分层设计提高了系统的易维护性、可扩展性,减少代码的耦合程度。

 2011032314404252.png

4、模块详细设计:

(1)、业务日志模块:

① 定义一个BaseAction.java类,所有的action都继承这个类。

在BaseAction中定义了一些公共的方法,子action只需调用即可。

businessLog方法就是在BaseAction中定义的一个方法,它负责记录业务日志。子类action中调用这个方法,并传递相应的业务信息,实现了记录业务日志的功能:

public void businessLog(String actionTarget,String actionType,String actionContent) throws SuitdiyException{

        String userId = getUserIdFromUserInfo();

        Map<String,Object> session = ActionContext.getContext().getSession();

        String sessionId = (String) session.get("sessionId");

        String ip = getIpAddr(ServletActionContext.getRequest());

        Date time = new Date(System.currentTimeMillis());

        suitdiyService.businessLog(sessionId, userId , ip,

                actionTarget, actionType, actionContent, time);

    }

② 业务逻辑层:

public void businessLog(String sessionId, String userId, String ip,

            String actionTarget, String actionType, String actionContent,

            Date time) throws SuitdiyException {

        try {

            businessLogDao.save(sessionId, userId, ip, actionTarget,

                    actionType, actionContent, time);

        } catch (Exception e) {

            e.printStackTrace();

            throw new SuitdiyException("记录日志过程中出错~");

        }

    }

③ dao层:

public void save(final String id, final String userId, final String ip,

            final String actionTarget, final String actionType,

            final String actionContent, final Date time) {

        getHibernateTemplate().executeWithNativeSession(

                new HibernateCallback<Object>() {

                    public Object doInHibernate(Session session) {

                        session.doWork(new Work() {

                            public void execute(Connection connection)

                                    throws SQLException {

                                CallableStatement cs = connection

                                        .prepareCall("{call saveBusinessLog(?,?,?,?,?,?,?,?)}");

                                cs.setString(1, UUID.randomUUID().toString());

                                cs.setString(2, id);

                                cs.setString(3, userId);

                                cs.setString(4, ip);

                                cs.setString(5, actionTarget);

                                cs.setString(6, actionType);

                                cs.setString(7, actionContent);

                                cs.setTimestamp(8, new java.sql.Timestamp(time

                                        .getTime()));

                                cs.execute();

                            }

                        });

                        return null;

                    }

                });

    }

(2)、购物车模块:

① 设计思路:

匿名用户即可以使用购物车,与账户无关;

购物车中产品信息的保存采用session+cookie的方式实现。购物车中产品信息保存在服务器端,客户端的cookie只保存购物车的id号。

产品信息不保存在cookie中的原因是,第一,cookie容量大小有限制,本网站产品信息包括了各种定制信息、尺寸信息、体型信息等,很容易就超过了这个限制;第二,cookie会被包含在每次的请求的请求头信息中,cookie内容多了会占用带宽资源。

数据库设计了两个表session_temp和product_temp,用来保存购物车及其产品的信息,如下所示:

Session_temp(id,last_update)

Product_temp(id,session_id,... ... )

具体的实现流程是,当客户端提交请求后,服务器端会首先判断session中是否有sessionId(自己设置的,用来唯一地识别客户端浏览器的id号,与购物车一一对应,可以看做是购物车id号),如果有,取出对应的id值,从Product_temp表中取出对应的产品信息,返回;如果没有,则判断cookie中是否有_suitdiy_session_id(自己设置的,用来唯一地识别客户端浏览器的id号,session中的sessionId值就是这个值的复制),如果有,则将这个id号放入session中(名字为sessionId),同时插入Session_temp中,返回;如果cookie中没有id号,则重新创建一个id号(使用uuid,全球唯一),放入session和cookie中并插入session_temp表中,返回。

_suitdiy_session_id这个id号没有用服务器端的sessionId,而是自己生成的uuid,原因是,sessionId在服务器重启的情况下可能会产生重复,造成混乱。

另外,购物车信息初始情况下是隐藏的,可以采用异步方式加载购物车信息,提高页面响应速度。在页面加载完成后,客户端发送一个ajax请求,获取购物车的信息。

② 存在的问题及解决方案:客户端删除cookie后,服务器端购物车的信息就成为了废物,网站点击量大了,会产生巨大的垃圾,占用了资源。解决方法是,在session_temp表中设置字段last_update,用户每次访问时,都更新这个字段;然后在数据库中设置一个定期执行的任务,每天晚上零点执行一次,判断last_update的值距离现在是否已经超过一定期限(期限可以根据实际运行情况进行调整),如果超过,删除,同时删除Product_temp表中对应的产品记录。

③ 购物车的行为:在客户端没有删除浏览器cookie的情况下,并且在距离最后一次访问本站的时间没有超过一定期限的情况下,服务器能永久地跟踪客户端特定的浏览器,其购物车信息不会丢失(业务日志中记录了浏览器的行为,为用户行为分析提供了依据)。

④ 代码实现:

1)   页面加载完成后,提交ajax请求获取购物车信息,并初始化购物车的交互行为的js代码: function InitCart(){

        $('#detail_cart').hide();

        var time = new Date().getTime();

        var url = 'cart.action';

        $('#detail_cart').load(url,function(){         $('#cart_num').html($('#detail_cart .count').html());

        });

        $('#menu_cart').click(function(){

            $('#search_input').slideUp();

            $('#detail_cart').slideDown('normal');

            return false;

        });

        $('body').click(function(){

            $('#detail_cart').slideUp('normal');

        });

        $('#detail_cart').click(function(event){

            event.stopPropagation();

        });

        $('#detail_cart a#delete').click(function(){

            if(confirm('确定要删除吗?')){

                return true;

            }

            else{

                return false;

            }

        });

    }

2)   处理cart.action请求的action代码:

a、BaseAction中的判断逻辑:(BaseAction实现了Preparable接口,prepare方法是Preparable接口的方法,它在action的任何方法执行之前执行,保证了判断逻辑的执行。)

public void prepare() throws SuitdiyException {

        Map<String, Object> session = ActionContext.getContext().getSession();

        sessionId = (String)session.get("sessionId");

        if(null != sessionId && !sessionId.equals("")){

            suitdiyService.updateSessionTemp(sessionId);

        }

        else{

            Cookie[] cookies = ServletActionContext.getRequest().getCookies();

            if(null != cookies){

                for(Cookie c:cookies){

                    if(c.getName().equals("_suitdiy_session_id")){

                        sessionId = c.getValue();

                    }

                }

            }

            if(null != sessionId && !sessionId.equals("")){

                session.put("sessionId", sessionId);

                suitdiyService.updateSessionTemp(sessionId);

            }

            else{

                sessionId = UUID.randomUUID().toString();

                suitdiyService.createSessionTemp(sessionId);

                session.put("sessionId", sessionId);

                Cookie c = new Cookie("_suitdiy_session_id",sessionId);

                c.setPath("/");

                c.setMaxAge(999999999);

                ServletActionContext.getResponse().addCookie(c);

            }

        }

}

b、子action获取购物车信息:

public class CartAction extends BaseAction{

    protected List<Map<String,String>> cartgoods = new ArrayList<Map<String,String>>();

    public String execute() throws SuitdiyException{

        cartgoods = suitdiyService.getCartgoodsBySessionId(sessionId);

        return SUCCESS;

    }

    public List<Map<String, String>> getCartgoods() {

        return cartgoods;

    }

    public void setCartgoods(List<Map<String, String>> cartgoods) {

        this.cartgoods = cartgoods;

    }

}

3)业务逻辑代码

public void updateSessionTemp(String sessionId) throws SuitdiyException {

        sessionTempDao.updateSessionTemp(sessionId);

    }

public List<Map<String, String>> getCartgoodsBySessionId(String sessionId)

            throws SuitdiyException {

        try {

            List<Map<String, String>> cartgoods = new ArrayList<Map<String, String>>();

            List<ProductTemp> pts = productTempDao.getBySessionId(sessionId);

            Map<String, String> countMap = new HashMap<String, String>();

            countMap.put("count", new Integer(pts.size()).toString());

            cartgoods.add(countMap);

            int iMax = (pts.size() <= 4) ? pts.size() : 4;

            for (int i = 0; i < iMax; i++) {

                ProductTemp pt = pts.get(i);

                Style s = pt.getStyle();

                Map<String, String> cg = new HashMap<String, String>();

                cg.put("productTempId", pt.getId());

                cg.put("styleName", s.getName());

                cg.put("frontPicture", s.getFrontPicture());

                cg.put("price", s.getPrice().toString());

                cg.put("count", pt.getCount().toString());

                String diy = s.getDiyOptions();

                if (null != diy && !diy.equals("")) {

                    cg.put("hasDiy", "yes");

                } else {

                    cg.put("hasDiy", "no");

                }

                Blob measure = pt.getMeasurements();

                if (null != measure && measure.length() > 0) {

                    cg.put("measureEmpty", "false");

                } else {

                    cg.put("measureEmpty", "true");

                }

                cartgoods.add(cg);

            }

            return cartgoods;

        } catch (Exception e) {

            e.printStackTrace();

            throw new SuitdiyException("获取购物车信息时出现错误~");

        }

    }

4)dao层代码

public void updateSessionTemp(final String sessionId) {

        getHibernateTemplate().executeWithNativeSession(

                new HibernateCallback<Object>() {

                    public Object doInHibernate(Session session) {

                        session.doWork(new Work() {

                            public void execute(Connection cnn)throws SQLException {

                                CallableStatement cs = cnn.prepareCall("call updateSessionTemp(?)");

                                cs.setString(1, sessionId);

                                cs.execute();

                            }

                        });

                        return null;

                    }

                });

    }

public List<ProductTemp> getBySessionId(String sid){

        return (List<ProductTemp>)getHibernateTemplate().find("from ProductTemp pt where pt.sessionTemp.id = ?",sid);

    }

5)调用的存储过程:

DELIMITER $$

DROP PROCEDURE IF EXISTS `suitdiy`.`updateSessionTemp`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE  `suitdiy`.`updateSessionTemp`(IN `sessionId` varchar(255))

BEGIN

    if (select count(id) from session_temp where id = sessionId) = 0 then

        insert into session_temp(id,last_update) values(sessionId,now());

    else

        update session_temp set last_update = now() where id = sessionId;

    end if;

END $$

DELIMITER ;

(其他省略。。。)

六、关键问题分析与解决

1、购物车采用session + cookie的方式实现。

2、管理人员在删除产品、定制项目、定制系列时,实际的操作不能直接删除。因为过去可能有对应的订单,订单对应的数据库表的外键指向这些产品、定制项目、定制系列对应的表,会出现删除异常。如果级联删除对应的订单记录,那么用户发现自己以前的订单没了,大事不妙。解决的方法是,在产品、定制项目、定制系列对应的表中设置一个字段enabled,用来标识一条记录是否被删除。删除时,仅仅把这个字段设置为false;查询时,加上一个条件enabled = true ;不影响订单继续使用这些记录。

3、在注册、创建订单、订单开始生产、订单生产完毕、订单已经发货后,系统会自动给用户发送一封邮件通知。这个用jms和javamail实现。发送邮件的任务由jms服务器以异步的方式完成,提高系统交互速度,提高客户体验。

4、本网站整合了一个开源的论坛系统,discuz7.2。由于这个论坛是用php做的,所以需要apache服务器。本系统整合了tomcat服务器和apache服务器。

5、struts中action的问题:有些页面使用了struts2校验框架后,如果校验失败,则立即返回,而execute方法没有执行,造成一些数据没有被初始化。解决方法有多种:

① 在action中重写validate方法,将初始化动作放在这个方法里面;

② 让action实现preparable接口,将初始化动作放在prepare方法里面。

6、本网站使用spring security框架进行权限管理。默认的userDetails实现类只包含username、password和权限等信息,通过提供自定义的userDetailsService类,使用自定义的UserInfo类,达到扩展信息的目的。

7、由于定制信息的条目比较多(大约有34个),如果每个条目对应一个字段,会造成数据库表的字段太多而不利于管理、维护,所以,本项目中,将所有的定制信息保存到java中的HashMap对象中,然后将其序列化后保存为数据库的一个Blob类型的字段。

转载于:https://www.cnblogs.com/waibustudio/archive/2011/03/23/document.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值