• $2015 武汉森果公司web后端开发实习日记----书写是为了更好的思考


      找暑期实习,3月份分别投了百度和腾讯的实习简历,都止步于笔试,总结的主要原因有两点:基础知识不扎实,缺乏项目经验.后来到拉勾网等网站上寻找实习,看了很多家,都还是处于观望状态.后来参加了武汉实习吧在大活举办的实习分享会,听完后最大的收获是获取了更多的信心.再过了几天偶然看到了年级群邮里发的一篇We信水果帮的实习招聘,加上发现是之前认识的学长发的邮件,于是和学长了解了一下情况,把简历发出去了.没过两天5月15日就收到了面试通知.面试之后,心里一阵紧张,毕竟是第一次经历求职面试,不过还算顺利,面试通过.不巧的是恰逢考试周,考试周考完两门后就到公司开始了正式的实习. 

    【5.27】

      下午考完期中考试最后一门接口技术,晚上和陈明一起到森果公司办公室,做到快十点,主要是配置系统开发的环境。今天完成的主要内容:

      1、 熟悉Github的用法:clone代码到本地(需要命令行命令)。

      2、 clone森果网站的代码到本地,并配置本地数据库,跑通入口程序。在乔迁学长的帮助下,了解网站架构,把网站的功能和代码进行对比。

      3、 产生的困惑:由于对tornado框架和sqlalchemy框架的不熟悉,导致对代码的理解不是很好,后来回去跟廖思敏学长聊,学长建议先把这两个框架官网上的Demo都跑通再说。此外还有对python的熟悉。

    【5.29】

      这两天每天都是早上9点半左右到公司,一直到晚上八九点.29号早上下雨了,整个上午天都是阴沉沉的,不过很凉快.水果帮网站在这两天也新增了功能,有一个功能就是在微信里实现支付宝支付,黑科技~赞一个~还有就在这几天支付宝的光纤被挖掘机挖断了,携程的服务器被攻击了,互联网安全的问题凸显出来了.对了今天李晨和范冰冰发微博公开恋情出现'我们体'.

      这两天做的事情有如下几点:

      1、 继续熟悉整个系统.

      2、 使用和了解worktile团队协作工具.

      3、 试着实现商家后台订单管理中根据不同条件对订单进行筛选和排序的功能.

      4、 初步完成了3中的代码,等待调试.但在调试的过程中遇到了无法登录本地网站服务器的问题,29号下午调试了一下午都没有解决,主要排查了nginx的配置 ,hosts文件,数据库数据等,反反复复试错了很多次,到晚上还没有解决.最终发现是因为昨天学长给的数据库并没有包含之前在网站注册的数据,因此登录不上,这算是一个低级的错误.想到的解决方案是:重新注册以后并绑定手机号,然后从乔迁学长那里用新的数据库进行本地测试.

      5、 初步熟悉postman(chrome浏览器)和httprequest(firefox浏览器)这两个web开发工具的使用.

      6、 今天相关的资料和文档记录在:①快盘的Daily文档;②worktile的Daily文档;③firefox的Daily书签里.

    【5.30】

      考试周完太累了,想休息一下,跟学长请假一天.主要是给妹子装数据库,教她数据库.在教的过程中自己也小小的复习了一下.

    【5.31】

      继续到公司上班,今天只有两个学长在,其他小伙伴都休息了.今天做的主要事情有:

      1、 调试系统网站的本地测试,依然没有成功.

      2、 深入练习了GitHub的使用,并测试提交代码与版本控制的功能(主要参照了'廖雪峰的官网'上的GitHub教程进行练习,总结在这篇博文里面:http://www.cnblogs.com/jiayongji /p/4542388.html

      3、 今天的文档记录在:worktile的Daily文档.

    【6.1 周一】

      今天解决了困扰两天的问题,重新导入了学长给的线上版数据库,成功解决了之前不能登录/admin的问题. 但偶尔出现了退出店铺后台不能再次登录的问题,解决方法是用登录页面上找回密码的功能发手机验证码重新设置密码,然后再到登录页面用手机号和新密码登录即可.所做的工作有:

      1、 进入系统进行订单管理新增功能的功能测试.在反复调试中掌握了前后端交互数据的形式.需要注意的有以下几点:

        ①在不断往本地系统中添加新数据后,尽量要经常备份数据库.最好本地和网盘上都备份一份.在终端中用这个命令备份数据库:$ mysqldump -uroot            -p*** abc > abc.sql(导出数据库abc到当前目录下保存为abc.sql文件,’***' 表示mysql的登录密码)

        ②pyton3 app.py –debug=1 #调试模式,每次修改代码后不用重启即可实时更新.

        ③测试后台的单独功能的时候直接在地址栏中添加和改变参数的值进行测试,不要在页面进行点击测试!否则会出错!!

        ④多次测试的时候因为URL参数中多了空格而出错,空格很不起眼,但仍然会导致页面打不开!!这点在后面的测试的时候一定要注意!!

      2、 在实现多表连接查询的排序功能的时候,多次尝试失败,到晚上仍没有找到解决方案,主要是因为对SQLAlchemy框架的操作不熟练,需要恶补框架知识.

      3、 今天的记录文档在:①Worktile Daily文档;②浏览器Daily书签;③快盘今日备份

    【6.2 周二】

      早上起来看新闻看到湖北客轮沉没,船上有四百多人,而且多为老年人,祈祷.

      今天所做的工作有:

      1、 初步完成了店铺后台订单管理中的订单排序与筛选的功能,之所以说初步,是因为后端功能完成了,还没有与前端进行整合,而且代码的效率还可以进一步优化,主要是在数据库查询方面的优化,要求接下来继续深入学习sqlalchemy的相关知识.

      2、 这周的新任务是:实现超级管理员后台的商铺管理功能,与店铺后台的代码逻辑比较相似,但是必须要充分优化效率,以应对大数据量.

      3、 进一步熟悉了整个系统的代码结构和数据库数据结构,但是对于表的联合查询的效率优化方面还有很多事情可做.

      4、 今天的相关文档记录在:①Worktile的Daily Tips文档中;②浏览器的Daily书签中,文件备份在快盘的Backup daily中.

     【6.3 周三】

      今天下午和晚上所做的工作有:

      1、 熟悉超级管理员后台的代码逻辑,重点熟悉店铺管理的代码逻辑和数据逻辑.

      2、 试着修改原有代码,进行小的功能测试.

      3、 实现了部分排序功能,但因为数据量大,数据库查询效率低下,急需寻找高效率的方法或算法进行优化.

        现在想到的思路有:

        ①通过sqlalchemy对数据库的shop表编制索引,提高查询效率.

        ②尝试使用海量数据排序算法,对数据排序进行优化.

      4、 sqlalchemy的知识还很欠缺,急待加强,可以看今天浏览器书签中的几篇博文进行加强.

      5、 店铺后台订单管理订单筛选排序功能的后端代码已经完成,前端实现推迟.

      6、 今天的文档记录在:①Worktile的Daily Tips文档中;②浏览器的Daily书签中,文件备份在快盘的Backup daily中.

    【6.5 周五】

      这两天主要实现总后台店铺管理功能,到6.5晚上已经基本完成,留下了两个难点问题还没有解决.这两天的工作总结如下:

      1、 这两天修改的数据库字段有:增加shop表的shop.fans_count(粉丝数)和shop.shop_property(店铺销售额)两个字段,sql脚本在快盘今日备份文件夹中(明天预计还要增加的字段是满意度字段).
      2、 完成了后台店铺管理中的店铺排序和筛选功能,功能测试都正确;完成了店铺信息卡显示数据的大部分处理(综合满意度是个复杂的点,还没有攻克;还有按店铺号或店铺名搜索的功能也没有实现,这个的实现方法可以参照店铺后台订单管理中的订单搜索功能的实现).
      3、 要学习前端知识,从前端到后端的整个开发流程还是要亲身经历一遍的,而且现在前端缺人手,自己要试着实现一些简单的功能.看W3CSCHOOL上的前端html+css+js教程,或者到CSDN上找些入门的系列教程看.

      4、 陈明的ubuntu升级到15.10以后,重启后进入不了系统,到windows下发现系统所在盘的所有文件都不见了,怀疑是在更新的时候感染了病毒,只好重装系统,费时费力.而且装ubuntu的时候最好装LTS稳定版的系统,最新版的系统一般不是很稳定.

        so,保持每天云端文档备份和文件备份的好习惯.

      5、 今天文档保存在:woktile 项目中;文件保存在:快盘DailyBackup中.

      6、 今天要整理快盘一周来的Backup,腾出本地空间.

    【6.6 周六】

      今天把总后台店铺管理的两个难点问题解决了,主要做的工作及总结如下:

      1、 完成了总后台店铺综合满意度的计算问题,解决方案是参照/list页面店铺满意度的计算方法.

      2、 完成了总后台按照店铺号或店铺名称搜索的功能,实现方法是在url增加search键,假定将search框中输入的关键字通过search传给后台,后台根据关键字对数据库进行筛选,筛选完毕以后再将数据返回给前台.主要代码如下:

     1 if 'search' in self.args:  #如果url中有'search' 2             from sqlalchemy.sql import or_   #导入sqlalchemy的or_,用于下方的判断店铺号或者店铺名是否等于搜索关键字
     3             search = self.args['search']    #获取search的值
     4             q = self.session.query(models.Shop).filter(or_(models.Shop.shop_name.like("%{0}%".format(self.args["search"])),   #数据库查询,将查询结果赋给q
     5                        models.Shop.shop_code.like("%{0}%".format(self.args["search"]))),
     6                        models.Shop.shop_status == models.SHOP_STATUS.ACCEPTED,    #要是申请成功的店铺
     7                        models.Shop.shop_code !='not set',models.Shop.status !=0 ).all()       #店铺号非空(!='not set')并且店铺不是关闭中
     8             shops = q
     9 else:
    10             q = self.session.query(models.Shop)   #否则将整个Shop表赋给shops
    11             shops = q.order_by(models.Shop.id).all()

      3、 下午学习了html/js/css的初步知识,熟悉了一下总后台前端的代码逻辑,为下一步编写总后台的前端代码做准备.

      4、 学长推荐用谷歌浏览器进行前端开发,但安装了谷歌浏览器,修改nginx配置文件后,可是仍然登录不了总后台,这是个疑难杂症,所以还是继续用火狐.

      5、 今天的文档文件和笔记记录备份在:wortile项目文件中和Daily项目中,快盘Daily备份,firefox Daily书签.

    【6.7 周日】

      高考第一天,湖北数学惊现文言文大题;晚上韵苑体育馆青春琴缘音乐会.

      今天主要是学习前端相关知识和总后台店铺管理数据分析,主要做的工作如下:

      1、 学习javascript基本语法,H5基本语法(主要在W3SHOOL上学).

      2、 继续研究前端代码结构,要接着学习javascript/h5/css/jquery.

      3、 新任务:在完成总后台店铺管理前-后端以后,再接着实现总后台余额管理的前-后端功能(与陈明协作).

      4、 今天的文件、文档备份在:worktile Daily,快盘Daily,firefox书签Daily.

      5、 总后台店铺管理数据分析:
        *将整形的sort_order映射成False(0)或True(1)
        *CustomerShopFollow表和orders表的链接查询,用于筛选新/老用户
        *筛选order_type和选定order_type相同的订单
        *根据sort_key和sort_order对orders进行排序
        *根据选定的支付方式对orders进行筛选'
        *如果选定的order_status=5(所有订单)

        (1)店铺状态(status=Column(Integer,default = 1) (0:关闭 1:营业中 2:筹备中 3:休息中)
          #键值对:"shop_status:int"
          所有(shop_status=5,shop.shop_status in [0,1,2,3,4])
          未激活(shop_status=4,shop_code = "not set")
          筹备中(shop_status=2 ,shop.shop_status=2)
          营业中(shop_status=1,shop.shop_status=1)
          休息中(shop_status=3,shop.shop_status=3)
          已停业(shop_status=0,shop.shop_status=0)

        (2)认证状态(shop_auth =Column(Integer,default =0)(0:未认证 1:个人认证 2:企业认证 3:个人认证转企业认证 4:企业认证转个人认证)
          键值对:"shop_auth:int"
          所有(shop_auth = 4,shop.shop_auth in [0,1,2,3,4])
          已认证(shop_auth = 3,shop.shop_auth in [1,2,3,4])
          个人认证(shop_auth = 2,shop.shop_auth in [1,4])
          企业认证(shop_auth = 1,shop.shop_auth in [2,3])
          未认证(shop_auth = 0,shop.shop_auth=0)

        (3)排序规则:
          键值对:
          "shop_sort_key:int"
          值:
            0:注册时间
            1:评价数
            2:账户余额
            3:粉丝数

            4:订单数
            5:营业额
            6:客单价

          [注册时间]:create_date_timestamp
          [评价数]:old_msg
          [账户余额]:available_balance
          [粉丝数]:
          *原来:self.session.query(models.CustomerShopFollow).filter_by(shop_id=shop.id).count()
          *增加字段后:shop.fans_count

          [订单数]:order_count
          营业额:
          *原来:self.session.query(func.sum(models.Order.totalPrice)).filter_by(shop_id=shop.id).scalar()
          *增加字段后:shop.shop_property

          [客单价]:营业额/订单数
          增加字段:single_price

        (4)排序顺序:
          键值对:"if_reverse:int"
          值:
            0:升序
            1:降序

          键值对默认值:
            1.shop_status:5(所有)
            2.shop_auth:5(所有)
            3.shop_sort_key:4(默认按订单数排序,降序)
            4.if_reverse:1(默认降序排列)
        (5)shop信息卡data数组:
          *店铺名(data["shop_name"] = shop.shop_name)(店铺名有超链接)

          *店铺管理员昵称(data["admin_nickname"] = shop.admin.accountinfo.nickname)(昵称有超链接)

          *店铺地址(data["shop_address_detail"] = shop.shop_address_detail)

          *auth_type_array = ['未认证','个人认证','个人认证转企业认证','企业认证转个人认证']
          *认证状态(data["auth_type"] = auth_type_array[shop.shop_auth])

          *认证信息(超链接)()

          *店铺号(data["shop_code"] = shop.shop_code)

          *create_date_trans = self.session.query(func.from_unixtime(shop.create_date_timestamp)).scalar()
          *注册时间(data["create_date"] = self.session.query(func.date_format(create_date_trans,'%Y-%m-%d')).scalar())

          *shop_status_array = ['关闭','营业中','筹备中','休息中']
          *店铺状态(data["shop_shop_status"] = shop_status_array[shop.status])

          *申请信息(超链接)
          *评价数(data["old_msg"] = shop.old_msg)

          *满意度()

     1 shop_id = shop.id
     2 orders = self.session.query(models.Order).filter_by(shop_id = shop_id ,status =6).first()
     3 if orders:
     4     q = self.session.query(func.avg(models.Order.commodity_quality),
     5 func.avg(models.Order.send_speed),func.avg(models.Order.shop_service)).filter_by(shop_id = shop_id).all()
     6 if q[0][0]:
     7     commodity_quality = int(q[0][0])
     8 if q[0][1]:
     9     send_speed = int(q[0][1])
    10 if q[0][2]:
    11     shop_service = int(q[0][2])
    12 if commodity_quality and send_speed and shop_service:
    13     satisfy = format((commodity_quality + send_speed +             shop_service)/300,'.0%')
    14 else:
    15     satisfy = format(1,'.0%')
    16 data["satisfy"] = satisfy

          *为了提高效率,在shop末尾新加入三种'满意度'字段:  ##突然感觉加这写没有必要,店铺满意度在/handlers/customer.py中已经有实现,为何不直接调用呢?或者直接用那种方法实现呢?新方法见上方.

    1 commodity_quality = Column(Float,default = 0,nullable = False) #value in 0~100
    2 send_speed = Column(Float,default = 0,nullable = False)  #value in 0~100
    3 shop_service = Column(Float,default = 0,nullable = False)  #value in 0~100

          *并写如下脚本更新数据库:

     1 use senguocc;
     2 alter table shop add column commodity_quality float(11) not null default 0;
     3 alter table shop add column send_speed float(11) not null default 0;
     4 alter table shop add column shop_service float(11) not null default 0;
     5 
     6 update shop set commodity_quality = 
     7 (
     8 select avg(senguocc.order.commodity_quality)
     9 from senguocc.order
    10 where shop.id = senguocc.order.shop_id
    11 );
    12 
    13 update shop set send_speed = 
    14 (
    15 select avg(senguocc.send_speed)
    16 from senguocc.order
    17 where shop.id = senguocc.order.shop_id
    18 );
    19 
    20 update shop set shop_service = 
    21 (
    22 select avg(shop_service)
    23 from senguocc.order
    24 where shop.id = senguocc.order.shop_id
    25 );

          *并在顾客购物后的处理方法中加入更新代码:
            goods_count=len(shop.fruits) + self.session.query(models.MGoods).join(models.Menu).filter(models.Menu.shop_id == shop.id).count()
          *商品件数(data["goods_count"] = goods_count)

          *粉丝数(data["fans_count"] = shop.fans_count)

          *订单数(data["order_count"] = shop.order_count)

          *营业额(data["shop_property"] = shop.shop_property)

    1 if shop.order_count == 0:
    2     single_price = 0
    3 else:
    4     single_price = shop.shop_property/shop.order_count

          *客单价(data["single_price"] = single_price)

          *账户余额(data["available_balance"] = shop.available_balance)

          order表中:

    1 commodity_quality = Column(Integer) #取值:0-100  
    2 send_speed = Column(Integer) #取值:0-100  
    3 shop_service = Column(Integer) #取值:0-100 
    1 select commodity_quality from order inner join shop on shop.id = order.shop_id where shop.shop_name like '吃在 东农%';

      6、 解决整形值做除法会变成0的方法:
        88/100:结果是0
        float(88)/100:结果是0.88
      7、 根据店铺号或店铺名搜索的功能:
        search=水果&shop_auth=4&shop_status=5&shop_sort_key=0&if_reverse=0&

      8、 今日数据:快盘.

    【6.9 周二】

      高考结束,相继出来替考、听力故障等事件。

      感觉这两天的状态有点手忙脚乱的感觉,一是自从开始研究前端的知识以及和后端的交互,都是一头雾水的,需要学习的东西太多,而又不怎么能抓住重点;二是课程的各种任务和实验,拖延症导致的疲于应对。今天git又出了点问题,明天要解决。

      看着学长和小伙伴们做的热火朝天的,各种新功能都实现了,解决了一个又一个问题,心中不免失落。不管怎样,这两天做的事情总结如下:

      1、 修正总后台店铺管理后端代码的bug和缺陷,研究与前端的交互。

      2、 完成前端店铺信息卡的修改和相关链接的跳转,但是跳转到的页面还是并不符合要求,这是下一阶段的工作。修改相关路由以与本地服务器相匹配(过后往git上推代码的时候还要改回来)。

      3、 陈明初步完成的店铺管理功能还有几个bug,此外还有与post请求和局部刷新等技术相关的难题没有解决。

      4、 firefox用的UA要为MicroMessenger,才能测试森果的微信申请功能。

      5、 html文件中可以调用py文件中的方法,举例如下(事先要先继承base.html):

    1 <p>
    2     <span class="tit1">序号:{{shop.id}}</span>
    3     <span>申请时间:{{handler.code_to_text("create_date_timestamp", shop.create_date_timestamp)}}</span>
    4 </p>

      6、 这两天的文档文件备份在:worktile Daily,快盘 Daily,Firefox书签。   

     【6.10 周三】

      昨天晚上写数据库的实验报告到很晚,今天一直都昏昏沉沉的。一早醒来看到森果昨天夜里更新的系统出了一些问题,微信群里都是商家在反映各种bug,学长们都在忙碌着解决问题。

      今天做的工作有:

      1、 研究前端通过post请求和ajax与后端进行数据交互,解决局部刷新的问题。

      2、 总后台店铺管理代码bug的排查,这要在post请求交互数据的代码中处理。

      3、 感觉对于js很不熟练,对于jquery框架也不是很熟悉,从前认为的前端开发就是写写html和css的想法完全是错误的,前端的重中之重是js。

      4、 学长又分配了新任务,实现总后台的订单统计功能,要用统计图的形式统计展现出来,这个可以参考店铺后台的订单统计的前端和后端的实现方法。

      5、 这一阶段要恶补一下js的知识(主要是敲w3school上的代码)。现在有个感受是后台的简单功能实现有很多现成的代码可以参考可以模仿,但是前端的实现以及与后端的交互这一块必须对原理有了透彻的理解后才能实现功能。

      6、 以后熬夜不能超过一点半,否则第二天完全是昏昏沉沉的状态,做什么都没有效率。

      7、 要整理一下worktile的文档了,现在开始显得有些杂乱了。

      8、 今天的文档文件备份在:worktile Daily,快盘Daily,firefox书签。

     【6.15 周一】

      今天所做的工作有:

      1、 实现了总后台的订单统计功能的python/js/html开发,绘出统计图表。发现echarts是个功能很强大的东西,特别在数据统计方面,功能很实用,很丰富,很酷炫。

      2、 修正了之前总后台店铺管理的几个BUG:

        ①shop表营业额字段的自动更新处理

        解决方法: 

        分别在staff.py和admin.py的shop.order_count += 1语句后添加:

          totalprice_inc = order.totalPrice 
          shop.shop_property += totalprice_inc

        经测试,原bug成功解决

        ②总后台店铺申请页面布局有错误

        经过反复测试发现是在有未处理的店铺申请的时候才会出现布局错乱,于是重点排查对店铺申请进行处理的html代码,发现了问题所在.

        解决方法: 
          apply-manage.html第86行左右{% end %}的位置错误,且多了两个右div。

        ③其他几个小BUG。

      3、 这两天每天实现新功能后就往git上推代码,经历过之前几天使用git的磕磕绊绊,现在已经能够熟练使用git推代码了,无非是三步曲:

        假设当前的本地工作分支为jyj.

        ①$ git fetch origin 远程分支名:tmp  (tmp为本地临时分支)  #把代码从远程分支上更新到本地的tmp分支上.

        ②$ git merge tmp  (将tmp与当前分支合并,如果有冲突,手动修改冲突)  #合并tmp分支到当前工作分支,并手动修改冲突.

        ③$ git push origin jyj:远程分支名  #将合并后的本地工作分支推到远程分支上,如果指定的远程分支不存在,则新创建一个分支.

        实现的新功能已经在测试服务器上跑起来了。

      4、 现在所用的从前端到后端的技术总结起来主要是这些:python后端-js中间层-html前端-css样式修饰.

      5、 python3和mysql的datetime数据类型好像不兼容,这个明天要和学长进一步讨论.

      6、 今天的文档和文件保存在:worktile Daily,快盘,书签Daily,github.

    【6.17 周三】

      近几日主要任务:总后台每日对账功能。

      1、 增加总后台页面的回到顶部按钮:
        *base.html
        *superadmin-global.css
        *global.js

      2、 每日对账html

    1 <!--count-balance.html-->
    2 <!--add by jyj 2015-6-16-->
    3 
    4 <li class="admin nav_item {{check_active}}">
    5 <a href="{{reverse_url('superCheckCash')}}">每日对账</a>
    6 </li>
    7 
    8 <!---->

      3、 遇到问题: Python 'result' cannot be converted to a MySQL type
        查询数据库返回的结果是一个元组,只需取第0个元素即可.

      4、 tornado的标签(if-else,for)

     1 {% if his["type"] == 2 %}
     2 <span class="tit1 orange-txt">-{{his["balance_value"]}}元</span>
     3 {% else %}
     4 <span class="tit1 orange-txt">+{{his["balance_value"]}}元</span>
     5 {% end %}
     6 
     7 for:
     8 {% for i in range(10)%}
     9 do something
    10 {% end %}

      5、 每日对账
        新建及修改文件:
        (1).superAdmin/count-balance.html
        (2).urls.py
        (3).superadmin.py: class CheckCash
        (4).superAdmin/balance-check.html
        (5).balance-check.js

      6、 加功能流程:
        ①先确定在哪个模板网页上加功能--->②在该网页上加上需要的html元素--->③在该网页的顶部设置该元素的激活状态变量,并设定激活条件,以及该元素的跳转href名称,这样写:href={{reverse_url
    ('superCheckCash')}}--->④在urls.py中添加该名称对应的路由以及superadmin.py中的新增加类的名称--->⑤在superadmin.py中增加新的类,实现后台功能.先从简单开始,只写get方法,return渲染到新页面.之后再丰
    富功能--->⑥添加名字为第⑤步中的渲染的网页名称的新网页,引用需要的基本框架,在其中添加简单的字符,以及在下方添加引用其对应的js文件--->⑦新建第⑥步中需要的js空文件--->⑧运行系统,看新增加的链接和
    页面能否正常加载.如果正常加载,则进一步丰富系统功能.

    <div class="col-sm-12 col-md-12"></div>

      7、 css垂直居中:.align-center{padding:1px;}

        html空格:&nbsp;
        html换行:<br>

      8、 html中js的src要放在继承的模板中才能生效.
        或者导入src的{%block%}块名称要与模板相同才能生效.

      9、 hide()和empty()方法均能让一个控件隐藏.

      10、 点击一个按钮:
          1.让一个控件或div隐藏或消失.
          2.往一个div中新增内容.
          3.获取输入框中的输入内容.
          4.引起一个post方法.

    1 $("#wx").text("lalala")
    2 var wx = $("#wx-income").val()
    3 wx = wx.toFixed(2);保留两位小数

      11、 js中加号被当成了连接符,如果要当做运算符,则先转换为整形或浮点型:
          parseFloat("123")=123

      12、 models.py中相关的类:
          class ApplyCashHistory 提现
          class BalanceHistory 通过record间接获取微信支付和阿里支付的钱数

    【6.18 周四】

      1、 postjson方法这样用:
        (1)在js中:

     1 function showCheckInfo(page){
     2   var url = "";
     3   var data = 1;
     4   var args={
     5     data:data
     6   };
     7   $.postJson(url,args,function(res){
     8     if(res.success){
     9       alert(res.output_data);
    10     }
    11   });
    12 }

        (2)在后台superadmin中:

    1 @tornado.web.authenticated
    2 @SuperBaseHandler.check_arguments('data:int')
    3 def post(self):
    4 data = self.args["data"]
    5 output_data = data + 333
    6 return self.send_success(output_data=output_data)

        运行后会弹出一个提示框,显示334.

        当然也可以把data封装成一个复杂类型的数据包:
        (1)js:

     1 function showCheckInfo(page){
     2   var url = "";
     3   var data={};
     4   data["a"] = 1;
     5   data["b"] = 2;
     6 
     7   var args={
     8     data:data
     9   };
    10   $.postJson(url,args,function(res){
    11     if(res.success){
    12       console.log(res.output_data["a"],res.output_data["b"]);
    13     }
    14   });
    15 }

        (2)后台:

    1 @tornado.web.authenticated
    2 @SuperBaseHandler.check_arguments('data') #这一行必须要写
    3 def post(self):
    4 data = self.args["data"]
    5 output_data = data 
    6 output_data["a"] = 555
    7 output_data["b"] = 666
    8 return self.send_success(output_data=output_data)

        在控制台输出:555 666

      3、 mysql获取当前日期函数:now() 返回年月日时分秒

      4、 对于日期的一个处理:

    1 now = datetime.datetime.now() - datetime.timedelta(1)
    2 start_date = datetime.datetime(now.year, now.month, now.day, 0)
    3 end_date = datetime.datetime(now.year, now.month, now.day, 23)
    4 q = q.filter(models.Order.create_date >= start_date,
    5 models.Order.create_date <= end_date)

      5、 mysql的有些函数sqlalchemy并不能用.

      6、 收入总额要加入余额消费的数据.
      7、 本来在对账的时候,是可以在js中根据加载完成后从后台get到的数据的控件的id得到然后进行比较的,不需要用post.但是考虑到数据精度,还是在js中向后台发post请求进行对账.

      8、 js通过post向后台传递的键值对的值都是str型的,如果需要比较大小等操作需要进行参数转化.但后台通过post向js返回的数据类型还是本来的数据类型.

      9、 python中format的结果是str型(千万要注意!),而round方法的结果才是字符型,但round方法只能用于浮点数.
        如:a = round(3.445,2),则a = 3.45,type(a)为float

      10、 考虑每天的对账信息都要记录下来,所以在数据库新建立一个对账表check_profit:

     1 create table check_profit(
     2 id int(5) primary key not null auto_increment,
     3 create_time datetime not null, 
     4 is_checked int(1) default 0,
     5 wx_record float(11) default 0,
     6 wx_count_record int(11) default 0,
     7 alipay_record float(11) default 0,
     8 alipay_count_record int(11) default 0,
     9 widt_record float(11) default 0,
    10 widt_count_record int(11) default 0,
    11 total_record float(11) default 0,
    12 total_count_record int(11) default 0,
    13 
    14 wx float(11) default 0,
    15 wx_count int(11) default 0,
    16 alipay float(11) default 0,
    17 alipay_count int(11) default 0,
    18 widt float(11) default 0,
    19 widt_count int(11) default 0,
    20 total float(11) default 0,
    21 total_count int(11) default 0
    22 );

      11、 sqlalchemy向数据库中加数据示例:

    1 balance_history = models.BalanceHistory(balance_record = '提现:管理员 '+name,balance_type =
    2 2,balance_value = apply_cash.value ,customer_id = apply_cash.shop.admin.accountinfo.id,name = 
    3 name,shop_id = apply_cash.shop_id,shop_totalPrice = shop.shop_balance,superAdmin_id = 
    4 self.current_user.id,available_balance = shop.available_balance)
    5 self.session.add(balance_history)
    6 self.session.commit()

      12、 数据库查询时offset和limit的用法:

    1 balance_history = self.session.query(models.BalanceHistory).filter(models.BalanceHistory.shop_id == shop_id,models.BalanceHistory.balance_type.in_([0,2,3])).order_by(desc 
    2 (models.BalanceHistory.create_time)).offset(page*page_size).limit(page_size).all()

        注意:offset和limit要用在filter,order_by等的后面.offset表示先取偏移量,然后limit表示从偏移量位置开始取多少个.常用于分页.而且偏移量是从0开始的.

      13、 python和js中的/不是整除,而是带小数的除法.

      14、 每天对账单的唯一性问题

    【6.20 周六】

      1、 data={}要放在循环里,否则会有诡异的错误
      2、 sqlalchemy delete操作用法:

    1 self.session.query(models.CheckProfit).filter(models.CheckProfit.create_time == end_date,models.CheckProfit.is_checked==0).delete()

      3、 js中attr
      4、 分页:两种分页方法的困惑.(页面加载时候就发post请求,在js中写渲染页面的代码的方案比较可行,简洁);js模板引擎.
      5、 table标签
      6、 js有时候有语法错但不提示,会导致莫名其妙的错误.
      7、 html中if-else等的用法;块的用法(tornado).
      8、 js中slice(m,n)函数相当于python中的切片操作.

      9、 js强制类型转化:parseInt(),parseFloat()
      10、 js post分页技术总结:
          (1)第1步:在html中建空表并用class(或id)标记,建上一页下一页按钮并用class或id标识:

     1 <div class="pull-left">
     2 <table class="tb-account">
     3 
     4 </table>
     5 </div>
     6 <nav>
     7 <ul class="pager mlf300">
     8 <li><a href="javascript:;" id="PrePage">&larr; 上一页</a></li>
     9 <li class="PageNow green-txt"></li>
    10 <li><a href="javascript:;" id="NextPage">下一页&rarr;</a></li>
    11 </ul>
    12 </nav>

          (2)第2步:在js中页面加载里面就引入渲染表格的方法showHistory(page),该方法需要向后台发post请求以得到返回数据,而且需要一个page参数:

     1 var page_sum = 0;
     2 var page_size = 20;
     3 var page = 1;
     4 $(document).ready(function(){
     5 getPageSum();
     6 showHistory(1);
     7 })
     8 showHistory(page)方法示例如下,其中用到了js模板引擎的技术:(一个小问题:在模板引擎中的html语句中怎样使用if-else 9 function showHistory(page){
    10 var url='';
    11 var args={page:page};
    12 $.postJson(url,args,function(res){ //post方法根据page参数获取要显示的数据
    13 if(res.success){
    14 var page_sum_now = parseInt(page_sum) ;
    15 if(page_sum_now < page_sum){
    16 page_sum_now = page_sum_now + 1;
    17 }
    18 $(".PageNow").text('-'+page+'/'+page_sum_now+'-');
    19 var history = res.history;
    20 $('.tb-account').empty(); //千万要注意每次刷新新的一页之前都要先清空之前的表格,否则显示的内容会累加,达不到分页的效果
    21 for(i = 0;i<history.length;i++){ //获取的数据history为一个列表,该列表的元素为字典
    22 his = history[i];
    23 
    24 var item= '<tr>' //待会儿要向html的表格中添加的项,其中的数据在后面需要渲染。width属性可以控制每一列显示的每个元素的宽度所占的百分比
    25 +'<td class="pl10" width="20%">店铺:<a href="/{{shop_code}}" title="点击查看该店铺余额详情" target="_blank" class=" text-blue">{{shop_name}} 
    26 </a></td>'
    27 +'<td width="17%">{{record}}{{name}}</td>'
    28 +'<td width="21%">{{order_num_txt}}<a href="/super/orderManage/" target="_blank" class="text-blue">{{order_num}}</a></td>'
    29 +'<td class="text-gray" width="15%">{{time}}</td>'
    30 +'<td class="txt-ar" width="12%"><span class="orange-txt">{{balance_value}}</span>元</td>'
    31 +'<td class="txt-ar pr10" width="15%"><span class="green-txt">{{balance}}</span>元</td>'
    32 '</tr>'
    33 var render=template.compile(item); //生成渲染器
    34 
    35 //给变量赋值
    36 var shop_code = his["shop_code"];
    37 var shop_name= his["shop_name"];
    38 if(shop_name.length >=8){
    39 shop_name = shop_name.slice(0,7) + '...';
    40 }
    41 var type = his["type"];
    42 var record = his["record"];
    43 var name = his["name"]
    44 if(name.length >=6){
    45 name =name.slice(0,5)+'...';
    46 }
    47 var order_num_txt = his["order_num_txt"];
    48 var order_num = his["order_num"];
    49 var time=his["time"];
    50 var balance_value = his["balance_value"];
    51 if(type == 2){
    52 balance_value = '-' + balance_value;
    53 }
    54 else{
    55 balance_value = '+' + balance_value;
    56 }
    57 var balance = his["balance"];
    58 
    59 //渲染表格项
    60 var list_item =render({
    61 shop_code:shop_code,
    62 shop_name:shop_name,
    63 type:type,    
    64 record:record,
    65 name:name,
    66 order_num_txt:order_num_txt,
    67 order_num:order_num,
    68 time:time,
    69 balance_value:balance_value,
    70 balance:balance
    71 });
    72 
    73 //向表格中添加渲染后的项:
    74 $('.tb-account').append(list_item);
    75 }
    76 }
    77 else{
    78 alert(res.error_text);
    79 }
    80 },
    81 function(){alert('网络好像不给力呢~ ( >O< ) ~');}
    82 );
    83 }

          (3)第3步:添加上一页和下一页按钮的click事件:

     1 $(document).ready(function(){
     2 getPageSum();
     3 showHistory(1);
     4 }).on("click","#PrePage",function(){
     5 if(page == 1){
     6 alert("当前已经是第一页了~");
     7 return false;
     8 }
     9 else{
    10 page--;
    11 showHistory(page);
    12 }
    13 }).on("click","#NextPage",function(){
    14 
    15 if(page >= page_sum){
    16 alert("当前已经是最后一页了~");
    17 return false;
    18 }else{
    19 page++;
    20 showHistory(page);
    21 }
    22 });

      11、 从数据库中删除数据示例:

    1 self.session.query(models.CheckProfit).filter(models.CheckProfit.create_time == end_date,models.CheckProfit.is_checked==0).delete()

      12、 其实如果对账成功,那么CheckProfit表中的记录值和实际值肯定是一样的;如果还没有对账,那么CheckProfit表中只会有记录值。所以向前台返回数据的时候没有必要把所有数据都返回,而只需要把记录的数据返回
    即可。
      13、 sublime中html还是识别不了注释。

      14、 经常将js和python的变量定义方法搞混...python的普通变量定义是可以直接拿来就用的,而js是要写var的,切记!!!js不写var的变量是全局变量。

      15、 今日数据:快盘,github.

    【6.21 周日】

      1、 mysql中日期格式转换函数:
        DATE_FORMAT(date,format)
        根据format字符串格式化date值。下列修饰符可以被用在format字符串中: %M 月名字(January……December)

     1 %W 星期名字(Sunday……Saturday)
     2 %D 有英语前缀的月份的日期(1st, 2nd, 3rd, 等等。)
     3 %Y 年, 数字, 4 4 %y 年, 数字, 2 5 %a 缩写的星期名字(Sun……Sat)
     6 %d 月份中的天数, 数字(00……31)
     7 %e 月份中的天数, 数字(0……31)
     8 %m 月, 数字(01……12)
     9 %c 月, 数字(1……12)
    10 %b 缩写的月份名字(Jan……Dec)
    11 %j 一年中的天数(001……366)
    12 %H 小时(00……23)
    13 %k 小时(0……23)
    14 %h 小时(01……12)
    15 %I 小时(01……12)
    16 %l 小时(1……12)
    17 %i 分钟, 数字(00……59)
    18 %r 时间,12 小时(hh:mm:ss [AP]M)
    19 %T 时间,24 小时(hh:mm:ss)
    20 %S 秒(00……59)
    21 %s 秒(00……59)
    22 %p AM或PM
    23 %w 一个星期中的天数(0=Sunday ……6=Saturday )
    24 %U 星期(0……52), 这里星期天是星期的第一天
    25 %u 星期(0……52), 这里星期一是星期的第一天
    26 %% 一个文字“%”。

      2、 datetime.timedelta(1)的结果为:1 day,0:00:00

      3、 balance_history表是不管每天有没有记录都会自动创建吗?
      4、 检查昨天的对账单是否创建,如果没有创建,说明balance_history的昨天的在线支付、用户余额充值的记录为空,说明昨天的对账单的记录值为0,这还是要创建check_profit表的昨天的数据的,只不过
    收入数据和提现数据每一项都为0.
      5、 artTemplate 原生语法是否支持if...else:
        这样的写法:
          <%if(…){%> html <%}else{%> html <%}%>
        或者这种写法:

          {if ...}html{else}html{/if}
        可是好像都用不了?
        正确写法:
          {{if ...}}...{{else}}..{{/if}}
      6、 模板引擎arttemplate
      7、解决artTemplate中不能使用if-else的问题:
        这样解决:
          (1)先提前判断条件type的取值:

    1 if(type == 2){
    2 var item2 = '<td width="17%">{{record}}{{name}}</td>';
    3 }
    4 else{
    5 var item2 = '<td width="17%">{{record}}<a href="/super/user" title="点击查看该用户详情" target="_blank" class=" text-blue">{{name}}</a></td>';
    6 }

          (2)然后将其加入item中:

    1 var item= '<tr>'
    2 +'<td class="pl10" width="20%">店铺:<a href="/{{shop_code}}" title="点击查看该店铺商品首页" target="_blank" class=" text-blue">{{shop_name}}</a></td>'
    3 +item2
    4 +...

          (3)再生成模板渲染引擎:

    1 var render=template.compile(item);

          (4)再按照原来的步骤做即可。

      8、 css遮罩层

      9、 js中动态生成的button用jquery怎样添加点击响应事件?
        (jquery获取不到动态创建的元素id)
        在js中动态生成了一个button,而且它的name是从数据库中获得是数据,也就是说我不知道当前点击的的button的id是什么,而且生成button是循环做的,一次会生成多个。我想用jquery获取他的name。 用js写的话可
    以onclick=function(this),想问用jquery怎么做?

      10、 让css遮罩层不随滚动条的滚动而消失的方法:
          将遮罩层的position样式改为fixed即可。

      11、 jQuery中在循环中绑定事件容易出现的问题.
          解决方法:

    1 ...(artTemplate模板引擎代码)
    2 $(".tb-history").append(list_item);
    3 $("#btn-check-"+i).click(function(i){
    4 return function(){
    5 alert(i+"xxxxxxxxxxxxx");
    6 }; //如果在这个btn点击以后还要监听其他按钮的click事件,则要把其他按钮放在这个click事件中,而且也要用这种形式的函数。
    7 }(i));

    【6.22 周一】

      1、 jquery循环绑定只能获取到最后一个元素的值的问题;js闭包问题;代理。
      2、 获取第i个子元素。
      3、 each方法遍历。
      4、 动态绑定的button的click事件中再添加其他button的click事件,但为什么连续发了好几个post请求??

      5、 重命名文件的命令:
        mv a.txt b.txt #把文件a.txt重命名为b.txt

      6、 总后台店铺余额详情和每日对账功能完成!
        小结:
        1.一开始的时候数据逻辑和功能逻辑没有理清楚,走了一些弯路。
        2.分页的时候本来打算用url传值的方法,但是因为每个item要处理的数据较多,而且里面的button有点击事件,这样的方式实现起来比较复杂。后来参考了一下系统的有一部分代码,觉得从页面加载开始就用post方法
    +artTemplate模板引擎渲染页面的方式会比较好,但会影响一些效率。post方法在刚刚开始做的时候比较顺利,但是后来遇到了循环动态绑定的问题,按照常规的方法无法获取到动态生成的元素的id,着实费了很大一番
    功夫才解决。在这个过程中遇到了js的代理、闭包等高阶的技术,到现在还是云里雾里的,需要好好学习一下,以后还要用到。
        3.因为要实现点击按钮弹出一个输入多个数据窗口的功能,一开始想的是js里有没有现成的这样的控件来用,后来发现还真的没有,在周兵学长的提示下,去网上查了js+css遮罩层的技术,发现正符合要求,于是仿照
    demo实现了这个功能。
        4.通过这个功能的实现,对新功能实现的从前台到后台的整个流程已经很熟悉了,对js/css/html三者的了解更多了,能在新功能中熟练操作它们。css的调试好费神。。
        5.html中table元素在显示列表的时候很规整,看起来很舒服,最好配合artTemplate引擎渲染。
        6.对js的测试技术感受很深:处处用alert...alert是个好东西,还是不会用firebug的调试功能。

      7、 新任务:总后台-用户。用户选项里,增加用户ID 、出生日期 的显示。 增加搜索:id、用户昵称

      8、 相关文件:superadmin.py,superadmin-user.js,user.html,user-item.html,superadmin-user.css,models.py
      9、 python将时间戳转化为日期:

    1 b_time_stamp = 776361600
    2 print(type(b_time_stamp))
    3 dateArray = datetime.datetime.utcfromtimestamp(b_time_stamp)
    4 birthday = dateArray.strftime("%Y年%m月%d日")
    5 print(birthday)

        输出:1994年08月08日

      10、 python中list方法

      11、 点击链接变成了绿色而并列的其他链接变回原来的颜色,这是怎么实现的?(当点击链接后链接变色,切换别的链接,又恢复原来样子)
        解决方案:
        1.首先在html中有类似这样的代码,表示3个链接(注意其中‘{{}}’中的变量):

     1 <li class="all nav_item {{detail_active}}">
     2 <a href="{{reverse_url('superBalance')}}">余额详情</a>
     3 </li>
     4 <li class="admin nav_item {{cash_active}}">
     5 <a href="{{reverse_url('superApplyCash')}}">提现申请</a>
     6 </li>
     7 <!-- add by jyj 2015-6-17 -->
     8 <li class="admin nav_item {{check_active}}">
     9 <a href="{{reverse_url('superCheckCash')}}">每日对账</a>
    10 </li>

        2.在html顶部写类似于这样的语句:

     1 {% set page = context.get("page", "detail")%}
     2 {% set detail_active = ""%}
     3 {% set cash_active = ""%}
     4 {% set check_active = ""%}
     5 
     6 {% if page == "detail"%}
     7 {% set detail_active = "active"%}
     8 {% elif page == "cash"%}
     9 {% set cash_active = "active"%}
    10 {% else %}
    11 {% set check_active = "active"%}
    12 {% end %}

        3.在后台实现该功能的类中,在get方法最后render的时候要有context属性,类似于这样:

    1 def get(self):
    2 ...
    3     return self.render("superAdmin/balance-check.html",output_data=output_data,context=dict(page='check'))

        4.在某一个相关的全局css中要有li标签的active属性的颜色值,或者a标签的visited属性的颜色值。

    【6.23 周二】

      1、 bug:总后台用户管理中点击‘关注的店铺’后跳转到一个错误页面:已解决!a标签的href属性错误问题。
      2、 bug:总后台店铺管理所有店铺中,点击搜索后搜索结果中的店铺名称消失了:已解决!在js中刷新搜索结果的页面时没有添加店铺名的a标签值。
      3、 js中即时搜索的实现:

    1 var input = $("#inputinfo");  //inputinfo表示搜索输入框的id
    2 input.on('keyup',function(){
    3         page=0;
    4         getSearchContent('search',$("#inputinfo").val(),page);
    5 });  //getSearchContent是向后台发出post请求并根据返回的数据渲染页面的函数。

      4、 sqlalchemy中将整形转化为字符串的函数:
        func.concat(models.Accountinfo.id,'')
      5、 sqlalchemy中like的用法:
        q = q.filter(or_(models.Accountinfo.nickname.like("%{0}%".format(inputinfo)),(func.concat(models.Accountinfo.id,'')).like("%{0}%".format(inputinfo))))
      6、 完成总后台用户管理搜索和完善信息卡功能!

      7、 数据备份:快盘.

    【6.24 周三】

      1、 bug:在测试服务器上看,总后台店铺余额详情页面上方的"提现中"数据有问题,显示的是当前系统最新的提现数据,而不是该店铺的正在提现的数据.

      2、 优化:总后台订单统计的送达时间统计的"昨日数据"的查询效率很低.

      3、 bug:超级管理员用户管理js报错:TypeError: fshop[j] is undefined:已解决!
        原因:在压缩用户名的时候,没判断用户关注的店铺是否为空,导致出现关注店铺列表不存在的情况.

      4、 mysql把时间戳转化为日期:
          mysql>SELECT FROM_UNIXTIME( 1249488000, '%Y%m%d' )  
          ->20071120
      5、 问题:python用如下方法把时间戳转化为日期结果少一天:

    1 b_time_stamp = 541008000
    2 dateArray = datetime.datetime.utcfromtimestamp(b_time_stamp)
    3 birthday = dateArray.strftime("%Y-%m-%d")

          输出为:1987-2-22
          而实际上应为:1987-2-23
          (好像是python的一个bug...)
        解决方法:用mysql的时间戳转换函数.
      6、 bug:总后台用户管理搜索以后再刷新页面再点下一页会没有东西,必须把搜索框中的内容删除掉才会正常:搜索框缓存清理问题.
        解决方法:在js的getContent方法开始加上一句代码:$("inputinfo").val('');  清空输入框缓冲区,即可解决问题.

      7、 数据,代码和文档备份:快盘.

    【6.29 周一】

      又是一个周一,发现已经两周没有更新博客了。之前一周多一直在加班和复习期末考试,再加上自身的拖延症,导致一搁置就是两周。不过技术上,之前每天的工作日志文档记录的都很详细,遇到、解决的问题和Bug也有记录;生活上,复习复习复习+实验报告实验报告实验报告,呃...当然每天和她在一起复习还是很棒很棒的。

      今天是期末考试的第一天,复习之余来点小总结放松一下。

      第一件事,前天下午去光谷创业咖啡参加的segmentfault组织的技术沙龙,真心不错。第一次参加这种外面的技术沙龙,各个领域的大神很多,除了抽了两次kindle都没有抽到和热浪滚滚的天气以外,其他都还是很愉快的,特别是森哥专门为沙龙提供的水果很好吃,很解渴。

      nodejs,x11,光谷社区,持续集成,单元测试,docker,自动化测试,GUI,开源字体github.com/ecomfe/fontmin...这些是关键词。一个小小的字体能玩出太多的花样,也可以搞各种优化;上古GUI——Alt+Tab的底层原来有那么多的细节要考虑,自以为熟练使用GTK原来还差的远着呢。

      深度科技的deepin操作系统印象深刻,感觉很适合程序员,爱折腾的程序员;圆桌会议的支持人贺钧大神原来是乔迁学长的哥们,世界略小。最后,光谷社区是个好地方。

      第二件事,这两天写实验报告真是写苦了,各种排版,不得已把当时组原课设的实验报告拿来,用其中已经设置好的各种样式当模板了,当时老谭真是下了一番功夫,不得不说组原课设的模板真的很赞。完成实验报告以后于是决定狠下心来把各种报告、论文写作的基本和高端的排版技巧学一学,以后(至少一年之内)到处用得到,随后会在另一篇博文中经常更新每天get到的新技能。

      第三件事,没有更新的两周的博客内容会在临近这周末的几天里总结出来,主要是各种技术问题。

    【7.4 周六】

      考完试后的第一天,感觉一下子轻松了不少.下午到公司,陈明因为家里有点事情要请一段时间的假,我自己在公司写了一下午和一个晚上代码.

      今天完成的任务和遇到的问题总结如下:

      1、 实现新功能:总后台余额详情表格中新增加一列,把所有店铺余额不为零的店铺名和店铺余额列出来,以及最近一次余额变动的时间.这个问题比较简单,只需从balancehistory表中查询,按照店铺id分组,查出每一组中create_time最大的项的店铺ID和店铺余额即可.可是在这个过程中遇到了问题,关于sqlalchemy的子查询和分组查询的问题,查出的数据一直不对,费了一番周折.最后想到了一个间接的方法,就是先把所有店铺的所有余额变动记录从balancehistory表中查询出来,并按create_time降序排序;然后用python对这个列表进行遍历,先设置一个空的记录表exist_id_list,每次循环先判断当前记录的shop_id是否在记录表中,如果不在,就把这条记录对应的shop_id加入记录表中,然后把shop_id,shop_totalprice,create_time三项数据插入一个字典中,然后把字典加入到一个列表中;如果在,就pass,进行下一轮循环.循环完毕后,得到的列表就是前端所需要的数据,再传给前端即可.

        可是做完以后检查发现数据还是有些问题,经过测试发现通过上述方法计算出的系统总余额与通过shop.shop_balance累加出来的系统总余额不一致,前者比后者要多,但是核查每个店铺的帐又都是一致的.这个问题还是要等周一的时候跟学长讨论一下.

      2、 今天遇到另外一个比较纠结的问题是,因为浏览器的cookie过期了,加上没有权限,之前能登进去的总后台今天突然登不进去了.安装了火狐浏览器的Advanced Cookie Manager插件查看cookie后才发现原来的cookie到6.26号已经过期了,而那些天正在期末考试没有发现.又折腾了好长时间才解决问题,解决的方法如下:

        ①安装火狐Advanced Cookie Manager插件。
        ②用学长的手机扫描i.senguo.cc的super登录页面二维码,此时登录cookie就已经存储在了cookie文件里。
        ③在刚刚安装的插件中找到zone.senguo.cc,删除它下面的所有cookie.再次登录zone.senguo.cc:8887/super,即可登录成功!    

      3、 今天还遇到这个类型问题:从表中用分组查询的方法查询年龄最大的人的姓名,正确的应该是这样:
        select name from infotable where birthday = (select min(birthday) from infotable);
        而不是:select name,min(birthday) from infotable;

        例:有people表:

          +----+------+-------+
          | id | name | grade |
          +----+------+-------+
          |  1 | aa   |    90      |
          |  2 | bb   |    95      |
          |  3 | cc   |    87      |
          +----+------+-------+

        从中查询grade最低的人的姓名,如果这样查:select name,min(grade) from people;  结果是:aa  87,显然不对;
        正确的应该是这样查:select name from people where grade=(select min(grade) from people);  结果是:cc,结果正确.

      4、 之前写的每日对账功能存在一个bug:线上系统缺了一周的数据,而且数据更新逻辑有问题.

      5、 js保留两位小数:number = number.toFixed(2)
        python保留两位小数:number = round(number,2)

      6、 今天发现senguo的git上有artTemplate的资料,大喜,要好好学习一下.

      7、 数据,代码和文档备份:快盘.

    【7.5 周日】

       今天同事们都放假,只有我一个人坐在那里写代码.因为晚上要赶一篇论文,所以下午四五点时候就回去了.完成的主要工作有:

      1、 修复昨天的bug:通过shop.shop_balance算出的系统总余额的账和通过balancehistory算出的帐有出入.
        问题所在:后者的查询逻辑有问题,数据库筛选的时候漏掉了一个等于0的条件.
      2、 修复了之前的一个bug:在线上服务器上看,总后台店铺余额详情页面上方的"提现中"数据有问题,显示的是当前系统最新的提现中的金额,而不是这家店铺的提现中金额.
        原因:superadmin.py的ShopBalanceDetail类的get方法中,cash_applying = self.session.query(models.ApplyCashHistory.value).filter(models.           ApplyCashHistory.has_done == 0,models.ApplyCashHistory.shop_id == shop_id).first()中的models.ApplyCashHistory.shop_id == shop_id条件没写,加上即     可.
      3、 今天要完成任务:添加以下模块:总后台-店铺-店铺评论
        ①那种选中后下面有一条绿线的效果是怎么做的.
          第一步:html中有这样的代码:

    1 <ul class="tab-lst group">
    2     <li class="active all-list">所有账单</li>
    3     <li class="cash-list">提现记录</li>
    4     <li class="charge-list">用户充值</li>
    5     <li class="online-list">在线支付</li>
    6     <li class="ballance-list">余额列表</li>
    7 </ul>        

          第二步:对应的css中有:

    1 .tab-lst{border-bottom: 2px solid #f6f6f6;-webkit-box-sizing: border-box;height:28px;}
    2 .tab-lst>li{font-size: 14px;color: #666;line-height: 28px;float:left;height:28px;width:70px; margin-right: 20px;cursor: pointer;text-align: center;}
    3 .tab-lst .active{border-bottom: 2px solid #52b43f;color: #52b43f;}

          第三步:js中为ul的li添加click事件:       

    1 .on("click",".tab-lst li",function(){
    2     var index = $(this).index();
    3     $(".tab-lst li").removeClass("active").eq(index).addClass("active");
    4 });

        ②让ul中的li横向排列:只需给每个li添加'float:left'属性:.head-choose>li{float: left;}
        ③原来的店铺评论页面布局有点错乱.<---ok
        ④仿照admin后台的条件选择按钮实现条件选择按钮.<---not ok
            太复杂了,还是用总后台店铺管理陈明写的那个选择按钮.
      4、 复制多个文件和文件夹夹到主文件夹下:sudo cp filea fileb -R file1 file2 file3 /home/jyj (filea,fileb为文件;file1,file2,file3为文件夹).

      5、 明天一定要把总后台的对账bug解决.

      6、 这几天要把这篇博客中的所有代码都改成插入代码的格式.

      7、 数据备份:快盘.

    【7.6 周一】

      今天完成了总后台的订单评价管理的功能,实现对系统所有订单评价的统计和筛选.学到的东西和遇到的问题总结如下:

      1、 点击页面中不同的选项按钮显示不同的子页面:将两个不同子页面的相同部分写成一个新的base,选项按钮中加a标签.然后将子页面分别写成几个不同的html文件,每个html文件都继承这个base,将那个新base中的a标签链接到新创建的html文件即可.

      2、 html中一个p标签占至少一行.

      3、 python字符串内置函数:split
        例:
          >>> s = 'a;b;c;d'
          >>> s.split(';')
          ['a', 'b', 'c', 'd']

      4、 tornado中的块(block)的用法:

        比如某个base.html中有一段共同的代码:

    1 {% block content %}
    2     <div>...</div>
    3 {% end %}

        如果想在继承它的子页面中的这段代码的下面插入东西,则要在上面代码的后面写上空块名,比如: 

    1 {% block shop %}  
    2 {% end %}

        合起来如下:  

    1 {% block content %}
    2     <div>...</div>
    3     {% block shop %}  
    4 
    5     {% end %}
    6 {% end %}

        然后在子页面中加入块shop,然后在shop块中写代码即可,如下:

    1 {% extends 'base.html'%}
    2 {% block shop %}  
    3     ...这是子页面代码...
    4 {% end %}    

        此外还要注意多级继承时块的命名和引用,页面排版格式的问题.

      5、 sqlalchemy用'!= None'来判断表的某一项不为空.

      6、 sqlalchemy的like用法:models.Shop.shop_name.like('果%')
        或者:models.Shop.shop_name.like("%{0}%".format(input_name))

      7、 postjson不能传datetime类型的数据的问题:
          用python的strftime函数转成字符串再传.
          用法:data["comment_create_date"] = order.create_date.strftime("%Y-%m-%d %H:%M:%S")
      8、 js中要保持id或class的唯一性,否则容易出现重复执行导致错乱.

      9、 jquery中$的详细用法学习,今天因为var变量的时候少了一个$耽误了好长时间.

      10、 用clone隐藏元素的方法更新页面的时候,如果遇到一个元素需要根据后台数据多次添加怎么办?
        解决方法:比如有一个p标签中有多个img标签,数量根据后台传来的变量i来确定,那么在js中只需用一个循环把多个img标签加入到一个字符串中,然后把这个字         符串append到p标签即可.

      11、 js中get方法的使用(参见系统代码文件superadmin-comment-info.js的initPagesum方法):
        简单描述如下:假设需要的数据为page_sum
        ①js中在页面加载完毕后执行这个函数:

     1 function initPagesum(){
     2     var url = '/super/comment_info?ajaxFlag=1';
     3     $.ajax({
     4         url:url,
     5         type:'get',
     6         success:function(res){
     7             if(res.success){
     8                 page_sum = res.page_sum;
     9                 alert(page_sum);
    10             }
    11         }
    12     });
    13 }

        ②后台对应的get方法要这样检查参数:

    1 @SuperBaseHandler.check_arguments("ajaxFlag")

        ③后台要先判断ajaxFlag的值是否为1,如果是则用send_success返回数据,否则用render返回数据:

    1 if ajaxFlag != '1':
    2     self.render('superAdmin/shop-comment-info.html',output_data = output_data,page_sum = page_sum,context=dict(count = {'del_apply':'','all_temp':'','all':'','auth_apply':''},subpage= "info"))
    3 else:
    4     return self.send_success(page_sum = page_sum)

      12、 sqlalchemy不能直接用shop_id in [1,2]这样的用法,而要这样用:
          shop_id.in_([1,2,3,4])

      13、 遗留bug汇总:

        ①总后台'店铺'栏下左侧的选项激活有问题.

        ②总后台对账的bug.

      14、 数据备份:快盘.

    7.7 周二

      1、 今日主要任务列表:修复总后台店铺页面中激活左侧选项不变成绿色的问题,以及括号中的数据错误问题.<---ok
        ①不变绿色问题:superadmin-shop-manage.js加载页面ready方法中多了这样一段代码:

    1 if(localStorage.getItem("itemIndex")){
    2 $(".shop-manage-nav li").removeClass("active").eq(localStorage.getItem("itemIndex")).addClass("active");
    3 }else{
    4 localStorage.setItem("itemIndex",0);
    5 $(".shop-manage-nav li").removeClass("active").eq(0).addClass("active");
    6 }

        ②括号中数据错误的问题:
        每个后台方法中数据返回的错误。
      2、 修复总后台对账的bug  <-----ok
      3、 实现点击一个图片后显示大图:
        思路1:添加图片的点击事件,用遮罩层显示原始大小的图片。
        思路2:给图片添加a标签,打开一个新的窗口显示原始图片:<a href="..." target="_blank"><img src="..."/></a>

      4、 要在浏览器中查看html加载后,某项后台数据的值的方法:

    1 <div class="admin-left text-right pull-left" data-info="{{context['subpage']}}">

        这样就可以在浏览器的调试器里查看data-info的值,耶可以在js中调用这个值了。

      5、 HTML怎么给图片加超链接,点击图后在另一页打开?

    1 <a href="..." target="_blank"><img src="..."/></a>

      6、 整理worktile上daily的文档的内容到每天日志中.

      7、 sublime2清除所有书签:Ctrl+Shift+F2

      8、 完成总后台评论管理功能!

      9、 html字体加粗:style="font-weight:bold"

      10、 mysql中limit用法:
        select * from table limit m,n
        其中m是指记录开始的index,从0开始,表示第一条记录
        n是指从第m+1条开始,取n条。
        例如:select * from tablename limit 2,4
        即取出第3条至第6条,4条记录

        *mysql分组查询并用limit的方法:

     1 假如有如下info数据库: 
     2 +----+------+
     3 | id | name |
     4 +----+------+
     5 | 1 | aa |
     6 | 2 | bb |
     7 | 3 | cc |
     8 | 4 | bb |
     9 | 5 | cc |
    10 | 6 | cc |
    11 +----+------+
    12 执行下列查询及结果如下:
    13 mysql> select * from info group by(name) limit 0,1;
    14 +----+------+
    15 | id | name |
    16 +----+------+
    17 | 1 | aa |
    18 +----+------+
    19 
    20 mysql> select * from info group by(name) limit 0,2;
    21 +----+------+
    22 | id | name |
    23 +----+------+
    24 | 1 | aa |
    25 | 2 | bb |
    26 +----+------+
    27 
    28 mysql> select * from info group by(name) limit 0,3;
    29 +----+------+
    30 | id | name |
    31 +----+------+
    32 | 1 | aa |
    33 | 2 | bb |
    34 | 3 | cc |
    35 +----+------+

      11、 python datetime和time的区别.
        ①字符串转化为time类型:

    1 >>> import time
    2 >>> timestr = "time2009-12-14"
    3 >>> t = time.strptime(timestr, "time%Y-%m-%d")
    4 >>> print t
    5 (2009, 12, 14, 0, 0, 0, 0, 348, -1)
    6 
    7 >>> type(t)
    8 <type 'time.struct_time'>
    9 >>>

        ②将time类型转化为字符串:

    1 now_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))

        ③time类型与datetime类型的转换:

    1 >>> import datetime
    2 >>> d = datetime.datetime(* t[:6])
    3 >>> print d
    4 2009-12-14 00:00:00
    5 
    6 >>> type(d)
    7 <type 'datetime.datetime'>
    8 >>> 

        ④常用格式化符号:

     1   %a 星期几的简写 Weekday name, abbr.
     2   %A 星期几的全称 Weekday name, full
     3   %b 月分的简写 Month name, abbr.
     4   %B 月份的全称 Month name, full
     5   %c 标准的日期的时间串 Complete date and time representation
     6   %d 十进制表示的每月的第几天 Day of the month
     7   %H 24小时制的小时 Hour (24-hour clock)
     8   %I 12小时制的小时 Hour (12-hour clock)
     9   %j 十进制表示的每年的第几天 Day of the year
    10   %m 十进制表示的月份 Month number
    11   %M 十时制表示的分钟数 Minute number
    12   %S 十进制的秒数 Second number
    13   %U 第年的第几周,把星期日做为第一天(值从0到53)Week number (Sunday first weekday)
    14   %w 十进制表示的星期几(值从0到6,星期天为0)weekday number
    15   %W 每年的第几周,把星期一做为第一天(值从0到53) Week number (Monday first weekday)
    16   %x 标准的日期串 Complete date representation (e.g. 13/01/08)
    17   %X 标准的时间串 Complete time representation (e.g. 17:02:10)
    18   %y 不带世纪的十进制年份(值从0到99)Year number within century
    19   %Y 带世纪部分的十制年份 Year number
    20   %z,%Z 时区名称,如果不能得到时区名称则返回空字符。Name of time zone
    21   %% 百分号

      12、 获取昨天的日期:lastday = datetime.datetime.now() - datetime.timedelta(1)

      13、 mysql 从datetime类型中取日期:
          在mysql中,我要取出当天录入的记录。里面录入时间: AddTime是datetime类型,假设其值为:2007-08-06 10:12:22
          select * from table1 where AddTime='2007-08-06'
          这里的时间AddTime要通过什么函数处理才能得到日期部分,从而可以和后面的日期能够比较。
          方法①:select * from table1 where TO_DAYS(AddTime)=TO_DAYS(NOW())
          方法②:SELECT * from taffair where date_format(TAffairDate,'%Y-%m-%d')='2007-04-04'

      14、 sqlalchemy distinct用法:persons = self.session.query(models.BalanceHistory.customer_id).distinct()
          或:persons = self.session.query(models.BalanceHistory).distinct(models.BalanceHistory.customer_id)
      15、 jQuery中this与$(this)的区别

      16、 MySQL 查询某个字段不重复的所有记录:

     1 //假设现在有如下6条记录 表名叫info
     2 +------+----+
     3 | name | id |
     4 +------+----+
     5 | aa | 1 |
     6 | bb | 2 |
     7 | cc | 3 |
     8 | bb | 4 |
     9 | cc | 5 |
    10 | cc | 6 |
    11 +------+----+
    12 //现在想从这6条记录中查询所有name不重复的记录
    13 select distinct name,id from book这样是不可以的 因为distinct只能作用于一个字段
    14 //答案: 
    15 select a.* from info a right join (
    16 select min(id) from info group by name) b on b.id = a.id
    17 where a.id is not null

      17、 ?sqlalchemy不能对datetime类型用distinct(这是什么问题咧???)
        用老办法:利用存在性数组,每次搜索是否在数组之中。
        缺陷:严重影响性能!有必要研究一下sql的distinct算法实现。
        根本原因还是js动态绑定的问题!

    7.8 周三】

      1、 今天任务:完成店铺后台的销售统计;总后台对账仍然有bug且查询效率很低:要重构代码。

      2、 echarts好酷炫.

      3、 echarts简析(标准条形图)
        1.图表数据都包含在一个Object对象option中

     1 option = {
     2     title : {  //图表标题和副标题
     3         text: '世界人口',
     4         subtext: '数据来自网络'
     5     },
     6     tooltip : {  //数据提示框
     7         trigger: 'axis'
     8     },
     9     legend: {  //图表图例
    10         data:['2011年', '2012年']
    11     },
    12     toolbox: {  //图表的右上角的工具箱
    13         show : true,
    14         feature : {  //控制显示哪些工具
    15            mark : {show: true},
    16            dataView : {show: true, readOnly: false},
    17            restore : {show: true},
    18            saveAsImage : {show: true}
    19         }
    20     },
    21     calculable : true,
    22     xAxis : [  //x轴的属性及数据
    23         {
    24             type : 'value',
    25             boundaryGap : [0, 0.01]
    26         }
    27     ],
    28     yAxis : [  //y轴的属性及数据
    29         {
    30              type : 'category',
    31             data : ['巴西','印尼','美国','印度','中国','世界人口(万)']
    32         }
    33     ],
    34     series : [  //图表中显示的数据,有若干个系列,数量及名称和legend的data对应
    35         {
    36             name:'2011年',
    37             type:'bar',
    38             data:[18203, 23489, 29034, 104970, 131744, 630230]
    39         },
    40         {
    41           name:'2012年',
    42              type:'bar',
    43              data:[19325, 23438, 31000, 121594, 134141, 681807]
    44         }
    45     ]
    46 }; 47

        2.option内容分析:
        (1)title:图表标题,每个图表最多仅有一个标题控件,每个标题控件可设主副标题。
          text(主标题)
          subtext(副标题)

        (2)tooltip:提示框,常用于展现更详细的数据.
          trigger:触发类型,默认数据触发,可选为:'item' | 'axis'

        (3)legend:图例,每个图表最多仅有一个图例。
          orient:布局方式,默认为水平布局,可选为:'horizontal' | 'vertical'
          x:水平安放位置,默认为全图居中,可选为:'center' | 'left' | 'right' | {number}(x坐标,单位px)
          y:垂直安放位置,默认为全图顶端,可选为:'top' | 'bottom' | 'center' | {number}(y坐标,单位px)
          backgroundColor:图例背景颜色,默认透明
          data:图例内容数组,数组项通常为{string},每一项代表一个系列的name,默认布局到达边缘会自动分行(列),传入空字符串''可实现手动分行(列)。
          使用根据该值索引series中同名系列所用的图表类型和itemStyle,如果索引不到,该item将默认为没启用状态。
          如需个性化图例文字样式,可把数组项改为{Object},指定文本样式和个性化图例icon,格式为

    1 {
    2   name : {string},
    3   textStyle : {Object},
    4   icon : {string}
    5 }

        (4)toolbox:工具箱,每个图表最多仅有一个工具箱.
          orient:布局方式,默认为水平布局,可选为:'horizontal' | 'vertical' .
          x:水平安放位置,默认为全图居中,可选为:'center' | 'left' | 'right' | {number}(x坐标,单位px)
          y:垂直安放位置,默认为全图顶端,可选为:'top' | 'bottom' | 'center' | {number}(y坐标,单位px)
          backgroundColor:工具箱背景颜色,默认透明
          padding:工具箱内边距,单位px,默认各方向内边距为5,接受数组分别设定上右下左边距,同css.
          itemGap:各个item之间的间隔,单位px,默认为10,横向布局时为水平间隔,纵向布局时为纵向间隔.
          itemSize:工具箱icon大小,单位(px),默认16px
          color:工具箱icon颜色序列,循环使用,同时支持在具体feature内指定color.

    1 feature : {
    2   mark : {show: true},
    3   dataView : {show: true, readOnly: false},
    4   magicType : {show: true, type: ['line', 'bar', 'stack', 'tiled']},
    5   restore : {show: true},
    6   saveAsImage : {show: true}
    7 }

        (5)xAxis : [
          {
            type : 'value'
          }
         ]
        (6)yAxis : [
          {
            type : 'category',
            data : ['周一','周二','周三','周四','周五','周六','周日']
          }
         ]
        (7)series(直角系):驱动图表生成的数据内容数组,数组中每一项为一个系列的选项及数据,其中个别选项仅在部分图表类型中有效,请注意适用类型。
          stack:组合名称,双数值轴时无效,多组数据的堆积图时使用,eg:stack:'group1',则series数组中stack值等于'group1'的数据做堆积计算。

        注:在series后面再加如下的一个color数组,则echarts中的图表元素的颜色会根据这个数组自动循环设置。

          color: ['#b6a2de','#2ec7c9','#5ab1ef','#ffb980','#d87a80',
             '#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa',
             '#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050',
             '#59678c','#c9ab00','#7eb00a','#6f5553','#c14089']

      4、 jQuery 遍历 - siblings() 方法:
        例:$("p").siblings(".selected") 查找每个p元素的所有类名为 "selected" 的所有同胞元素.

      5、 mysql比较时间:select create_time from check_profit where date_format(create_time,"%Y-%m-%d")<'2015-07-01';

      6、 导出数据库中某张表的结构及数据:mysqldump -uroot -pdbpasswd dbname test>db.sql;
        导入的话还是用source,导入的时候不用删除表结构,直接导入sql即可。导入后会自动覆盖旧数据,不用担心错乱的问题。

      7、 python获取今天的0:0:0:
          now_time = time.localtime(time.time())
          now_time = datetime.datetime(*now_time[:6])
          now_time = datetime.datetime(now_time.year, now_time.month, now_time.day, 0,0,0)
        获取昨天的0:0:0:
          last_time = now_time - datetime.timedelta(1)

      8、 改变图表标题下方三个选项按钮的激活状态,并且选择不同按钮的时候图表的时间选项发生变化:
        (1)html中写如下代码:

    1 <div class="change-box">
    2   <ul id="order_trend_change" class="sell-change-list pull-left">
    3     <li data-id="1"><a href="javascript:;" class="text-grey3">订单量/个</a></li>
    4     <li data-id="2"><a href="javascript:;" class="text-grey3">订单金额/元</a></li>
    5   </ul>
    6 </div>

        (2)js中添加如下代码:

     1 $(document).ready(function(){
     2   $('.change-list li').on('click',function(){
     3     var $this=$(this);
     4     $this.addClass('active').siblings('li').removeClass('active');
     5   });
     6   $('.change-list').each(function(){
     7     var $this=$(this);
     8     $this.find('li').eq(0).addClass('active');
     9   });
    10 });

        (3)css中有如下属性:

    1 .sell-change-list{margin-left:60px;margin-top: 10px;}
    2 .sell-change-list li{border-left:2px solid #fff;padding-left:14px;float:left;margin-right: 30px;}
    3 .sell-change-list li.active{border-color:#44b549;}
    4 .sell-change-list li.active a{color:#44b549;}

      9、 css的padding:
        padding 属性设置元素的内边距。
        padding 属性定义元素边框与元素内容之间的空间。
        该属性可采取 4 个值:
          如果规定一个值,比如 div {padding: 50px} - 所有四个边的 padding 都是 50 px。
          如果规定两个值,比如 div {padding: 50px 10px} - 上下内边距是 50 px,左右内边距是 10 px。
          如果规定三个值,比如 div {padding: 50px 10px 20px} - 上内边距是 50 px,左右内边距是 10 px,下内边距是 20 px。
          如果规定四个值,比如 div {padding: 50px 10px 20px 30px} - 上内边距是 50 px,右内边距是 10 px,下内边距是 20 px,左内边距是 30 px。

      10、 js时间操作:
        (1)获取当前日期及其他操作:

     1 var myDate = new Date();
     2 myDate.getYear(); //获取当前年份(2位)
     3 myDate.getFullYear(); //获取完整的年份(4位,1970-????)
     4 myDate.getMonth(); //获取当前月份(0-11,0代表1月)
     5 myDate.getDate(); //获取当前日(1-31)
     6 myDate.getDay(); //获取当前星期X(0-6,0代表星期天)
     7 myDate.getTime(); //获取当前时间(从1970.1.1开始的毫秒数)
     8 myDate.getHours(); //获取当前小时数(0-23)
     9 myDate.getMinutes(); //获取当前分钟数(0-59)
    10 myDate.getSeconds(); //获取当前秒数(0-59)
    11 myDate.getMilliseconds(); //获取当前毫秒数(0-999)
    12 myDate.toLocaleDateString(); //获取当前日期
    13 var mytime=myDate.toLocaleTimeString(); //获取当前时间
    14 myDate.toLocaleString( ); //获取日期与时间

      11、 js将字符串转化成日期的方法:

    1 var str ='2015-01-01 23:13:15';
    2 str = str.replace(/-/g,"/");
    3 var date = new Date(str );

      12、 js判断某天是这年的第几周:

     1 function WeekNumOfYear(date) {   
     2     var yy = date.getFullYear();
     3     var day = date.getDay();
     4 
     5     var date0 = new Date(yy,0,1);
     6     var date_diff = DateDiff(date,date0);
     7     if(date_diff < 7-day){
     8         week_num = 1;
     9     }
    10     else{
    11         var week_num = Math.ceil((date_diff-(7-day))/7)+1;
    12     }
    13 
    14     return week_num;
    15 }

      13、 js ceil() 方法可对一个数进行上舍入。
        Math.ceil(x)

      14、 js round() 方法可把一个数字舍入为最接近的整数。

      15、 js获取当前日期的前后N天日期的方法:

      1 // 获取当前日期的前后N天日期(返回值为Date类型)(N<=28):
      2 function GetDateN(date,AddDayCount) 
      3 { 
      4     var dd = new Date(2015,1,1);  //此处如果使用new Date(),则会出现诡异的错误
      5 
      6     var date_year = date.getFullYear();
      7     var date_month = date.getMonth()+1;
      8     var date_date = date.getDate();
      9 
     10     dd.setDate(date_date);
     11     dd.setMonth(date_month - 1);
     12     dd.setFullYear(date_year);
     13 
     14     var n_flag;
     15     var is_leap;
     16 
     17     if(AddDayCount >= 0){
     18         n_flag = 1;
     19     }
     20     else{
     21         n_flag = 0;
     22     }
     23 
     24     if((date_year % 4 == 0 && date_year % 100 != 0) || (date_year % 400 == 0)){
     25         is_leap = 1;
     26     }
     27     else{
     28         is_leap = 0;
     29     }
     30 
     31     switch(n_flag){
     32         case 1:
     33             if (date_month == 2){
     34                 switch(is_leap){
     35                     case 1:
     36                         if(date_date + AddDayCount > 29){
     37                             dd.setMonth(date_month);
     38                             dd.setDate(date_date+AddDayCount - 29);
     39                         }
     40                         else{
     41                             dd.setDate(date_date + AddDayCount);
     42                         }
     43                     break;
     44 
     45                     case 0:
     46                         if(date_date + AddDayCount > 28){
     47                             dd.setMonth(date_month);
     48                             dd.setDate(date_date+AddDayCount - 28);
     49                         }
     50                         else{
     51                             dd.setDate(date_date + AddDayCount);
     52                         }
     53 
     54                     break;
     55                 }
     56             }
     57             else if ((date_month == 1 || date_month == 3 || date_month == 5 || date_month == 7 || date_month == 8 || date_month == 10 ) && date_date + AddDayCount > 31){
     58                 dd.setMonth(date_month);
     59                 dd.setDate(date_date+AddDayCount - 31);    
     60             }
     61             else if(date_month == 12 && date_date + AddDayCount > 31){
     62                 dd.setDate(date_date+AddDayCount - 31);
     63                 dd.setMonth(0);
     64                 dd.setFullYear(date_year + 1);
     65             }
     66             else if ((date_month == 4|| date_month == 6 || date_month == 9 || date_month == 11) && date_date + AddDayCount > 30){
     67                 dd.setMonth(date_month);
     68                 dd.setDate(date_date+AddDayCount - 30);    
     69             }
     70             else{
     71                 dd.setDate(date_date + AddDayCount);
     72             }
     73         break;
     74         case 0:
     75             if ((date_month == 3) && date_date + AddDayCount <= 0){
     76                 switch(is_leap){
     77                     case 1:
     78                         if(date_date + AddDayCount <= 0){
     79                             dd.setMonth(date_month - 2);
     80                             dd.setDate(date_date + 29 + AddDayCount);
     81                         }
     82                         else{
     83                             dd.setDate(date_date + AddDayCount);
     84                         }
     85                     break;
     86 
     87                     case 0:
     88                         if(date_date + AddDayCount <= 0){
     89                             dd.setMonth(date_month - 2);
     90                             dd.setDate(date_date + 28 + AddDayCount);
     91                         }
     92                         else{
     93                             dd.setDate(date_date + AddDayCount);
     94                         }
     95 
     96                     break;
     97                 }
     98             }
     99             else if ((date_month == 2 || date_month == 4 || date_month == 6 || date_month == 8 || date_month == 9 || date_month == 11 ) && date_date + AddDayCount <= 0){
    100                 dd.setMonth(date_month - 2);
    101                 dd.setDate(date_date + 31 + AddDayCount);
    102             }
    103             else if(date_month == 1 && date_date + AddDayCount <= 0){
    104                 dd.setFullYear(date_year - 1);
    105                 dd.setMonth(11);
    106                 dd.setDate(date_date + 31 + AddDayCount);
    107             }
    108             else if ((date_month == 5|| date_month == 7 || date_month == 10 || date_month == 12) && date_date + AddDayCount <= 0){
    109                 dd.setMonth(date_month - 2);
    110                 dd.setDate(date_date + 30 + AddDayCount);
    111             }
    112             else{
    113                 dd.setDate(date_date + AddDayCount);
    114             }
    115         break;
    116     }
    117     
    118     // 
    119     var y = dd.getFullYear(); 
    120     var m = (dd.getMonth()+1)<10?"0"+(dd.getMonth()+1):(dd.getMonth()+1);//获取当前月份的日期,不足10补0
    121     var d = dd.getDate()<10?"0"+dd.getDate():dd.getDate(); //获取当前几号,不足10补0
    122     var str = y+"-"+m+"-"+d+" 00:00:00"; 
    123     str = str.replace(/-/g,"/");
    124     var new_date = new Date(str);
    125     return new_date;
    126 }

      16. firebug下可以成功使用console.log()

      17. css居中问题:
        div居中:
          margin: 0 auto;
        text居中:
          text-align:center;

      18. ?js深拷贝和浅拷贝问题.

      19. sublime快捷键:Ctrl+Shift+上/下键:将一行上移/下移.

      20. js计算两个日期的天数差:

     //计算天数差的函数,通用  
    function  DateDiff(sDate1,  sDate2){    
        var  oDate1,  oDate2,  iDays ;
        var dd1,dd2,mm1,mm2,yy1,yy2;
        dd1 = sDate1.getDate();
        mm1 = sDate1.getMonth();
        yy1 = sDate1.getFullYear();
        dd2 = sDate2.getDate();
        mm2 = sDate2.getMonth();
        yy2 = sDate2.getFullYear();
    
        oDate1  =  new  Date(yy1,mm1,dd1) ;   
        oDate2  =  new  Date(yy2,mm2,dd2) ; 
        iDays  =  parseInt(Math.abs(oDate1  -  oDate2)/1000/60/60/24);    //把相差的毫秒数转换为天数  
        return  iDays;  
    }

      21、 今日备份:快盘。

     [7.14 周二]

      昨天从安徽黄山旅游5天回来,今天是回来的第一天上班。主要完成的工作为:店铺后台销售统计的前端日/周/月的时间显示与选择功能,为之后的后端数据查询与分析功能奠定了基础。

      1、 bug:总后台店铺评论不应该包括系统自动评论的订单
        修改方案:把状态为7(系统自动评价)的订单去掉。
      2、 判断闰年:if((year%4==0&&year%100!=0)||(year%400==0))

        即:年份能被4整除但不能被100整除,或者能被400整除。
      3、 js 单独set年月日时要先set month再set date
      4、 js date类的setMonth方法的诡异问题(setMonth后月份顺延问题):

        如果原来date的月份是偶数月,则重新setmonth后月份会比正确值多1,而奇数月不会有这个问题。
        如:date原来为2015-08-31,经过date.setMonth(8)以后,date变成了2015-10-01,而不是2015-09-01。
        解决方案:在创建date的时候不要使用空的构造函数:var date = new date();
        而要用类似于这样的构造函数:var date = new date(2015,1,1);
        这样问题就会解决。
      5、 js中的整除运算:
        Math.ceil(count / pagesize);   //向上整除 4/3=2;
        Math.floor(count / pagesize);   //向下整除 4/3=1;
      6、 创建日期对象的方法之一:
        var date = new Date(2015,0,1,23,59,59);
        其中0表示是1月份。
      7、 git本地仓库切换分支后,sublime中的代码会自动更新到切换后的分支。
      8、 发现最近经常和mysql、python、js的时间类型打交道,而用到的时候又不是很熟悉,而且非常容易搞混,所以要做个总结:

      (1)三种日期类型之间的相互转化的方法:
        ①MySQL datetime类型——>python的datetime类型
        ②python的datetime类型——>MySQL datetime类型
        ③js的date类型——>python的datetime类型
        ④python的datetime类型——>js的date类型
      (2)同种语言日期类型与字符串的相互转化:
        ①python中日期字符串——>python datetime类型
        ②python datetime类型——>字符串
        ③js中日期字符串——>js date类型
        ④js date类型——>js字符串
        ⑤MySQL中日期字符串——>MySQL datetime类型
        ⑥MySQL datetime类型——>MySQL字符串
      (3)同种语言时间戳和日期类型的相互转化:
        ①python时间戳——>python datetime类型
        ②python datetime类型——>python时间戳
        ③js时间戳——>js datetime类型
        ④js datetime类型——>js时间戳
        ⑤MySQL时间戳——>MySQL datetime类型
        ⑥MySQL datetime类型——>MySQL时间戳
      9、 店铺的水果信息在Fruit表中存。
      10、 字段关联的时候的查询方法。

      11、 今日备份:快盘

     【7.15 周三】

      今天主要做的工作是继续写店铺后台的销售统计的功能。昨天已经完成了前台最重要的功能之一:显示并能够选择不同的日/周/月,今天做的工作主要是数据需求和数据库的分析工作。

      1、 bug:总后台订单时间和送达时间统计的数据完全一样
         解决方法:测试的时候发现报错:'KeyError:24',经过排查发现是superadmin.py中orderstatic类中的数组下标越界问题,数组最大的下标为23,但是数据会出现24的下标,从而导致错误。
         改成这样即可:

    1  if order[0] == 1:  # 立即送收货时间估计
    2     if order[1].hour + (order[1].minute+order[3])//60 == 24:
    3       data[0] += 1
    4     else:
    5       data[order[1].hour + (order[1].minute+order[3])//60] += 1

      2、 今日任务数据需求与数据库分析:
        (1)销量排行:按商品类目排序 数据需求分析
          用户所选时间段(某天,某周,或某个月)内的该店铺(self.current_shop)的不同类目的商品的总销售额、该类目下的所有小类商品的销售额,以及数据的排序(按照类目总销售额从大到小排序)
          ①查询某个店铺所有水果的类目:

    1 select distinct fruit_type.name from fruit_type,fruit where fruit_type.id = fruit.fruit_type_id and fruit.shop_id = 1203;

          ②查询某个店铺的某个水果属于什么类目:

    1 select fruit_type.name from fruit_type,fruit where fruit.fruit_type_id = fruit_type.id and fruit.shop_id = 1203 and fruit.name = '青苹果';

          ③查询某个店铺的所有水果名称:

    1 select name from fruit where shop_id = 1203;

          ④从order表中查询出特定的一天某个店铺的所有有效订单(status字段的值大于等于5)的fruits字段:

    1 select fruits from senguocc.order where shop_id = 1203 and status >= 5 and ((create_date like "2015-07-15%" and today = 1) or (date_sub(create_date,interval -1 day) like "2015-07-15%" and today = 2) );

          ⑤从order表中查询出某个日期区间内某个店铺的所有有效订单(status字段的值大于等于5)的fruits字段(比如2015-07-15和2015-07-16两天的):

    1 select fruits from senguocc.order where shop_id = 1203 and status >= 5 and ( (create_date >= "2015-07-15" and date_sub(create_date,interval 1 day) < '2015-07-16' and today = 1) or (date_sub(create_date,interval -1 day) >= "2015-07-15" and create_date < "2015-07-16" and  today = 2) );

          ⑥查询某个店铺所有水果类目中分别有多少种水果:

    1 shop_type_num_list = []
    2 for shop_type_name in shop_all_type_name:
    3     tmp = {}
    4     shop_type_name = shop_type_name[0]
    5     tmp["type_name"] = shop_type_name
    6     tmp["type_num"] = self.session.query(models.FruitType).join(models.Fruit).filter(models.Fruit.shop_id == self.current_shop.id,models.FruitType.name == shop_type_name).count()
    7     shop_type_num_list.append(tmp)

          ⑦把⑥中查到的fruits字段中的每个单种商品的名称、数量、计价方式的单价分离出来,并计算出每个fruits中每个单种商品的销售额。

          *将fruits字段的字符串先转化为字典类型,然后从字典里面取值
          *注:订单的status字段的值大于等于5的订单才能算销售额
          *是不是应该有个运费收入
          *order表有一个today字段,today=1表示送货时间是‘今天’(即和order的create_date在同一天),today=2表示送货时间是'明天'(即在order的create_date的后一天)

          *用户不能自己创建新的类目,如果用户的商品不在现有的类目中,则只能归为'其他'类。
        (2)销量排行:按商品名称排序 数据需求分析
          用户所选时间段(某天,某周,或某个月)内的该店铺(self.current_shop)的不同名称的商品的销售额,以及数据的排序(按照商品销售额从大到小排序)
        (3)单种水果的销售额统计
        (4)单个类目水果的销售额统计

      3、 python如何将字符串转换成字典dict类型:
        用eval()或exec()函数实现:
          >>> user
          "{'name' : 'jim', 'sex' : 'male', 'age': 18}"
          >>> b=eval(user)
          >>> b
          {'age': 18, 'name': 'jim', 'sex': 'male'}
          >>> exec("c="+user)
          >>> c
          {'age': 18, 'name': 'jim', 'sex': 'male'}


      4、 mysql date_sub()函数举例:
        假如今天是2013年5月20日。
        date_sub('2012-05-25',interval 1 day) 表示 2012-05-24
        date_sub('2012-05-25',interval 0 day) 表示 2012-05-25
        date_sub('2012-05-25',interval -1 day) 表示 2012-05-26
        date_sub('2012-05-31',interval -1 day) 表示 2012-06-01
        date_sub(curdate(),interval 1 day) 表示 2013-05-19
        date_sub(curdate(),interval -1 day) 表示 2013-05-21
        date_sub(curdate(),interval 1 month) 表示 2013-04-20
        date_sub(curdate(),interval -1 month) 表示 2013-06-20
        date_sub(curdate(),interval 1 year) 表示 2012-05-20
        date_sub(curdate(),interval -1 year) 表示 2014-05-20

      5、 python获取当前时间:now = datetime.datetime.now()
        获取当前时间的年月日:now_date = datetime.datetime(now.year,now.month,now.date)

      6、 这样解决之前的echarts没有满足要求的堆积图表的难题:
        第一步先把所有类目的水果的总销售额用条形图显示出来;
        第二步为每个类目的条形图添加点击事件,当点击的时候弹出新的图层显示该类目不同种类水果的详细销售额。

      7、 mysql日期或时间类型可以直接与日期或时间字符串比较大小。
        比如今天是2015-07-15,则:

          select curdate()>'2015-07-10'的值为1,select curdate()>'2015-07-19'的值为0.
          select now() > '2015-07-10'的值为1,select now() < '2015-07-10'的值为0.

      8、 今日备份:快盘

      【7.16 周四】

      今天完成的主要工作有:店铺后台销售统计的按照商品类目统计和按照商品名称统计的功能已经基本实现。

      1、 python的下面这段代码执行的时候会出现错误: now_date = datetime.datetime(now.year,now.month,now.date) TypeError: an integer is required (got type builtin_function_or_method)

    1 now = datetime.datetime.now()
    2 now_date = datetime.datetime(now.year,now.month,now.date)

        解决方法:把now.date改为now.day即可。

      2、 sqlalchemy join查询的总结:
        (1)当两个表之间只有一个外键的时候,这样用:

    1 session.query(User).join(Address).
    2 ...
    3     filter(Address.email_address=='jack@google.com').
    4 .
    5     all() 

        (2)当两个表之间没有外键或者有不止一个外键的时候,这样用:

    1  query.join(Address,User.id==Address.user_id)# 明确的条件
    2  query.join(User.addresses)# 指定从左到右的关系
    3  query.join(Address,User.addresses)    #同样,有明确的目标
    4  query.join('addresses') # 同样,使用字符串

            outerjoin()和join()用法相同:

    1 query.outerjoin(User.addresses)   # LEFT OUTER JOIN

      3、 两个很有用的前端网站:bootstrap,iconfont

      4、 python将字符串转化为datetime类型:

    1 end_date_str = '2015-07-16'
    2 end_date = datetime.datetime.strptime(end_date_str,'%Y-%m-%d')

      5、 python计算当前时间向后10天的时间:

    1 d1 = datetime.datetime.now()
    2 d2 = d1 + datetime.timedelta(days =10)

      6、 python遍历取得字典的value的方法:

    1 for key in dic_list:
    2     value = dic_list[key]

        注:这里的'key'可以写成其他的字符,比如aa,bb等。
          此外字典可以用变量当做索引。

      7、 python 只获得字典中所有键的名字到列表中:

    1 d={'a': 2, 'b': 3, 'd': 4}
    2 dlist=list(d.keys())

        用字典的keys方法获得所有键的名字,python3需要转换为list,python2直接为list

        (是不是还有个values方法??)
      8、 js给后台传的参数设计:

        (1)action,5个取值:'all','type','name','single_type','single_name'
        (2)type,3个取值:'day','week','month'
        (3)start_date
        (4)end_date
        注:感觉type没有必要要.

      9、 python列表排序:

    1 listaa.sort(key = lambda aa_item:len(aa_item),reverse=True)  #按照列表listaa的每一项的长度的降序排列.

      10、 今天遇到一个问题:在js中将Date类型值通过postJson传给python后台后,发现这个值莫名其妙地少了一天,后来经过学长的提醒,发现应该是因为js和python所用时区不一样,从而导致了日期不一致的问题。
        解决方案:在js中通过postJson传Date值前,先将它转化成字符串再传。自编字符串转化函数:

    1 function getDateStr(date){
    2     var y = date.getFullYear();
    3     var m = (date.getMonth()+1)<10?"0"+(date.getMonth()+1):(date.getMonth()+1);//获取当前月份的日期,不足10补0
    4     var d = date.getDate()<10?"0"+date.getDate():date.getDate(); //获取当前几号,不足10补0
    5     var str = y+"-"+m+"-"+d;
    6     return str;
    7 }

      11、 研究一下echarts条形图的点击事件
      12、 js中for(var i = 0;i < data.length;i++)和for(var i in data)的区别。

      13、 今日备份:快盘

      [7.17 周五]

      1、 今天主要任务:店铺后台销售统计。

      2、 问题:图表所在的div的高度不能根据图表内容的增多自动增高

        解决方法:在图表init之前就发post请求获得要显示的数据,然后根据数据的多少动态设置div的高度,然后再init图表即可。
        设置div高度的方法:

    1 $("#div-name").css("height",50*data.length + 'px');

      3、 今天用到的技术:
        (1)echarts条形图的点击事件
        (2)遮罩层的实现
        (3)下拉选项按钮
        (4)echarts图表的属性设置

      4、 页面刷新后图表的y坐标轴的文字被遮挡了一部分,而且横坐标的值也没有显示出来。
        问题所在:options的series[0]的itemStyle属性设置的问题
        解决方案:
          原来的echarts的itemStyle属性为:

    1 itemStyle : { normal: {label : {show: true, position: 'insideRight'}}}

          改为如下这样即可解决问题:

     1 itemStyle : { 
     2     normal: {
     3         label : {
     4             show: true, 
     5             position: 'right',
     6             textStyle : {
     7                 fontWeight : 'bold'
     8             }
     9         }
    10     }
    11 }    

      5、 js把数值字符串转化为浮点型并保留两位小数(后来发现toFixed最后的结果的类型是字符串):

    1 var price = parseFloat("123.4567").toFixed(2);

      6、 发现个问题:如果echarts图表所在的div的高度太小了那么echarts也显示不出来。

      7、 今天下午遇到一个奇怪的问题:电脑过一个多小时弹出一个对话框,然后强制自动关机一次,很不爽。。CPU也不热呀,要找到原因。而且这几天键盘还有好几个键失灵,目测是短路了,找时间去修一修。

      8、 问题:echarts图表能显示最大值最小值,但就是显示不出来平均值。
        经过反复测试发现:echarts在求平均值的时候竟然不能对浮点数求平均值,只能是整数。
        最后才发现:原来js对浮点数执行toFixed方法的结果是字符串,并不是数值型,这才导致了不能求平均值。
        比如:var aa = 123.456.toFixed(2);则用typeof aa可以发现aa的类型为string,并不是浮点型,要把aa在转换成浮点型,要用parseFloat(aa)函数。
        把toFixed方法执行过的结果先用parseFloat强制转换成浮点型,再push给echarts的series的data,则可以解决问题。

      9、 这几天公司经常断网,要下个echarts的离线文档,防止断网的时候不能看。

      [7.19 周日]

      今天把店铺后台的销售统计功能成功完成!

      1、 标签属性中添加的onclick事件传递参数的问题,传递的参数是整个标签吗?

      2、 下拉选择按钮的实现总结。

        这个实现使用的bootstrap的控件元素。

        ①在css中编写如下样式:

     1 .mlf20{margin-left: 20px;}
     2 .dropdown-menu{overflow: hidden;max-height: 210px;overflow-y: auto;}
     3 .category-list>li{float: left;width: 25%;}
     4 .cate-title{font-size: 14px;color: #999;height: 28px;line-height: 28px;float: left;margin-right: 5px;}
     5 .slide-down-select{display:inline-block;width:100%;background:#fff;color:#000;border:1px solid #ddd;margin-left:0;}
     6 .slide-down-select .slide-btn{height: 24px;line-height: 24px;display: block;border: none;outline: none;width: 100%;}
     7 .slide-down-select em{font-style:normal;display:inline-block;width:110px;font-size:14px;overflow:hidden;padding-left: 10px;text-align: left;}
     8 .dropdown-toggle{padding:0 4px;}
     9 .dropdown-menu li{width:100%;padding:0;}
    10 .dropdown-menu .item{padding-left:14px;}
    11 .dropdown-menu li a:hover{color:#fff;background-color: #6ba4ef;}
    12 .caret{margin-top:10px;color: #c6c6c6;}
    13 .borderc{border: 1px solid #e7e7eb;}
    14 .pointer{cursor: pointer;}
    15 .min80{min-width: 80px;}
    16 .mt2{margin-top: 2px;}
    17 .txt-center{text-align: center !important;}
    18 .txt-left{text-align: left !important;}
    19 .hig{border: 1px dashed #ff6666 !important;}
    20 .no-title{font-size: 20px;text-align: center;margin-top: 40px;color: #333;}
    21 
    22 .pull-left{float:left !important;}
    23 .w130{width:130px !important;}

        ②在html中要加入下拉选择按钮的地方写如下代码,其中最外的是li元素,第一个div是选择按钮前的提示文字,第二个div中:button代表下拉选择按钮最上面显示的按钮,em标签代表强调;button下面的ul标签是隐藏的几个按钮,当点击最上方按钮的时候隐藏的按钮出现,ul的标签内容是在js中动态添加的。      

     1    <li> 
     2         <div class="tit cate-title">选择商品类目:</div>
     3         <div class="dropdown slide-down-select pull-left w130">
     4                 <button class="btn btn-default dropdown-toggle area height25 slide-btn" type="button" id="dropdownMenu3" data-toggle="dropdown">
     5                     <em class="pull-left filter_status" id="first_type"></em>
     6                     <span class="caret pull-right no-margin-left rotate0"></span>
     7                 </button>
     8                 <ul class="dropdown-menu dropdown-menu-right w130 condition-list" role="menu" aria-labelledby="dropdownMenu3" id="currentTypeName">
     9                 </ul>
    10         </div>
    11     </li>

        ③在js中,为button及ul添加内容及添加点击事件:

        添加内容:

     1          
    2
    $("#first_type").text(type_max); 3 6 $("#currentTypeName").empty(); 7
    8
    9 for(var i = 0;i < all_type.length;i++){ 10 11 var type_id = "type"+i; 12 var item = '<li>' 13 +'<a class="item" id={{type_id}} onclick="onTypeItemClick({{type_id}})">{{type_name}}</a>' 14 '</li'; 15 var render = template.compile(item); 16 17 var type_name = all_type[i]; 18 var list_item= render({ 19 type_id:type_id, 20 type_name:type_name 21 }); 22 $("#currentTypeName").append(list_item); 23 24 }

        点击事件:

    1 function  onTypeItemClick(type_id){
    2     var first_type = $("#"+type_id.id).text();
    3     $("#first_type").text(first_type);
    4     
    5 
    6 }

      3、 为echarts的series动态添加新的系列:

    1 options.series.push({name:"aaa",stack:'总量',type:'bar',data:[220, 232, 101, 234, 190]});

      4、 js遍历object的方法:

    1 var obj = {"name":"jyj","age":22};
    2 for(var key in obj){
    3   console.log(key);
    4 }

      输出结果:
        name
        age

      如果改成这样:

    1 var obj = {"name":"jyj","age":22};
    2 for(var e in obj){
    3   console.log(obj[e]);
    4 }

      输出结果:
        jyj
        22 

      5、 echarts渲染之前xAxis的data不能为空,否则会出错。

      6、 几个小问题:
      (1)考虑到数据量大的时候的显示问题,为下拉选项框和前两个图表增加滚动条功能。     ---ok
      (2)前两个图表的每个条的高度小一点,20px左右。                   ---ok 因为有行间距,所以20太小,最后定为40px
      (3)为第三个图表增加echarts滚动条效果。                       ---ok
      (4)去掉按周排序的“第几周”。                              ---ok

      7、 让元素的高度达到一定值的时候出现滚动条:
        在该元素的class中添加如下css属性即可:

    1 .dropdown-menu{overflow: hidden;max-height: 100px;overflow-y: auto;}

      8、 要让echarts图表的高度变高到一定值的时候也出现正常的滚动条小果,那么不能直接在echarts的父div上加滚动条css,而要在父div外面再套一个div,再在这个div上添加滚动条css.

      9、 echarts datazoom数据区缩放控件的蓝色拖动条的宽度大一点(可是改变dataZoom控件的handleSize属性后不起作用,这是个问题)

      10、 ubuntu firefox的cookie文件位置:$HOME/.mozilla/firefox/xxxx.default/目录下的cookie.sqlite文件。

      11、 今日备份:快盘。

     [7.20 周一]

      1、 今日任务:余额记录重复、遗漏、数值计算错误的问题(错误多是因为并发操作没有加锁引起的)
        这几个表:balancehistory(余额变化记录),shop(shop_balance),customer_shop_follow(用户在某店铺中存的余额),order(是依据)

      2、 今天装公司新电脑ubuntu系统装完以后开机遇到这个问题,且每次开机都会出现:
        System program problem detected
        Do you want to report the problem now?

      3、 昨天的订单送达时间问题,今天测试又发现没有问题,还是要在多家店铺上面进行测试才行。

      4、 mysql将查询记录及结果记录写入到外部文件中的方法:
      方法1:
        使用tee命令(登入数据库的时候要加sudo):
          mysql > tee /home/senguo/log.txt;
          mysql > use mysql;
          mysql > show tables;
          mysql > notee; //关闭记录功能
      方法2(按照格式写入excel文件):
        select * from shop
        into outfile '/tmp/test.xls'
        fields terminated by ','
        optionally enclosed by '"'
        lines terminated by ' ';
        注:路径必须是/tmp/xxx.xxx,否则没有权限。

      5、 今日任务数据库分析:
      (1)shop表:
        *shop_balance字段:当前店铺的余额
        *available_balance:该店铺当前的可提现余额
        *正确的情况下:同一时刻available_balance <= shop_balanc

      (2)order表:
        *pay_type = Column(TINYINT, default=1)#付款方式:1:货到付款,2:余额 3:在线支付
      (3)balancehistory表:
        *balance_type = Column(Integer,default = 1) # 0:代表充值 ,1:余额消费 2:提现 3:在线支付 4:商家删除订单 5:用户自己取消订单 6:余额消费完成 ,可提现额度的变化
        *这个表是从2015-5-3开始有记录的。
      (4)customer_shop_follow表:
        *shop_balance:该用户在该店铺存的余额

      6、 python3相比python2的输入函数的变化:
        python3删除了raw_input,用input代替:
        2.X:guess = int(raw_input('Enter an integer : ')) # 读取键盘输入的方法
        3.X:guess = int(input('Enter an integer : '))

      7、 mysql用select 1 into outfile '/home/senguo/test/test.xls';命令将查询结果写入外部文件出错:ERROR 1 (HY000): Can't create/write to file '/home/senguo/test/test.xls' (Errcode: 13)
        修改了test文件夹权限:sudo chmod -R 777 /home/senguo/test仍然不行,如果先创建test.xls文件然后修改其权限为777,则在数据库中再执行命令:select 1 into outfile '/home/senguo/test/test.xls';又会出现错误:ERROR 1086 (HY000): File '/home/senguo/test.xls' already exists
        解决方法:在系统根目录下有一个'/tmp/'目录,将上面的sql命令的文件路径改为'/tmp/test.xls'即可成功执行。

      8、 ubuntu中在命令行下当不知道用什么程序打开某一个文件的时候,可以使用:xdg-open file.xxx 的命令自动用默认的对应的程序打开。
        比如:xdg-open test.xls,会自动用excel程序打开当前目录的test.xls文件。

      9、 mysql改变某一列:alter table senguocc.order change fruits fruits varchar(2000) default NULL;

      10、 店铺后台是根据浏览器cookie判断当前店铺是哪个店铺的,如果线上系统和本地系统同时运行,那么因为cookie的原因,会导致点击链接跳到另外一个店铺的问题。所以以后测试的时候还是不要同时开线上的和线下的比较好。

      11、 admin-sell-count bug:第三张单种类目销售统计表,当某种类目中的商品数非常多的时候,会出现图例(legend)很拥挤的情况,非常影响美观,因此最后去掉legend.(ok)

      12、 admin-sell-count bug:第三张单种类目销售统计表,当添加新商品并有销售记录后,前几天的销售记录的数据会出问题。
        主要原因:js中object的元素是无序排列的,python的字典也是无序排列的,后台给js传参的时候没有注意到这个问题。

      13、 python中把一个字典aa中的元素按照键名排序并存到一个列表中的方法:
        先获得字典aa的键名列表:bb = list(aa.keys())
        然后把列表bb排序:bb.sort()
        然后再以bb的元素为索引读取aa中的值:

    1 another_list = [ ]
    2 for i in range(len(bb)):
    3     tmp = { }
    4     print(bb[i],":",aa[bb[i]])
    5     tmp[bb[i]] = aa[bb[i]]
    6     another_list.append(tmp)

       14、 今日备份:快盘。

     [7.21 周二]

      1、 admin-sell-count a big bug:查询数据库的时候用的是商品名称而不是id导致的很多错误,而且还没考虑到商品的名称是可以修改的。经过大规模修改后才解决问题。

        这给了以后一个教训:所有数据库查询必须用不变且唯一的值进行操作(如id),不能想当然用自己觉得对的字段。

      2、 让echarts在后台查询数据的时候就显示loading画面的方法:
        将chart和options都定义成全局变量,先初始化chart和option,同时在初始化的function中定义loading函数,不隐藏,然后再发post请求,等到返回成功数据以后再往chart的option里面添加数据,添加数据后再隐藏loading函数,最后显示图表。

      3、 小问题:admin sell count前两个图表的排序方式和下面图表距离太近了,如果图表太高添加滚动条以后两者就挨在了一起,要增大间距。

      4、 python将整数强制转换成字符串:str(123)

      5、 python文件操作:
        (1)打开文件:f = open("./test.txt","w+")
        (2)向文件写入字符串:f.write("") (只能写入字符串,如果想把列表等用write函数写入文件,则要先转化成字符串才行。)
        (3)关闭文件:f.close()

      6、 今日备份:快盘。

      [7.22 周三]

      1、  今日任务:余额记录重复、遗漏、数值计算错误的问题(错误多是因为并发操作没有加锁引起的)
        这几个表:balancehistory(余额变化记录),shop(shop_balance),customer_shop_follow(用户在某店铺中存的余额),order(是依据)

      2、  js实现遮罩层:
        (1)首先在html body块中写如下代码:

    1 <body>
    2   <input id="btn-check" type="button" value="Showa" onclick="showdiv();"/> 
    3   <div id="checkinput_bg"></div>
    4   <div id="div-input">  //这个div中的内容为自定义的内容    
    5     <input id="btnclose" type="button" value="Close" onclick="hidediv();"/> 
    6   </div>
    7 </body> 

        (2)然后在css中写如下样式:

    1 <style type="text/css">
    2   #checkinput_bg{ display: none; position: absolute; top: 0%; left: 0%; width: 100%; height: 100%; background-color: black; z-index:1001;-moz-opacity:0.7;-khtml-opacity: 0.7;opacity:.70; filter: alpha(opacity=70);}
    3   #div-input{display: none; position: absolute; top: 25%; left: 22%; width: 53%; height: 49%; padding: 8px; border: 8px solid #E8E9F7; background-color: white; z-index:1002; overflow: auto;}
    4 </style>

        (3)最后在js中写如下代码添加按钮的回调函数,控制div的显示和隐藏:

     1 <script language="javascript" type="text/javascript">
     2   function showdiv() { 
     3     document.getElementById("checkinput_bg").style.display ="block";
     4     document.getElementById("div-input").style.display ="block";
     5   }
     6   function hidediv() {
     7     document.getElementById("checkinput_bg").style.display ='none';
     8     document.getElementById("div-input").style.display ='none';
     9   }
    10 </script>

      3、 SQLServer [sikəu 'sə:və]

      4、 mysql改变某一列的结构和属性:alter table senguocc.order change fruits fruits varchar(2000) default NULL;
        改变某一列的值:update student set name="jyj" where id = 001;

      5、 bug:

     1 File "/home/senguo/KuaiPan/Senguo/gitwork/senguo.cc/admin/handlers/admin.py", line 495, in post
     2 fruit_id = self.session.query(models.Fruit.id).join(models.ChargeType).filter(models.ChargeType.id == int(key)).all()[0][0]
     3 IndexError: list index out of range
     4 
     5 File "/home/senguo/KuaiPan/Senguo/gitwork/senguo.cc/admin/handlers/admin.py", line 608, in post
     6 fruit_id = self.session.query(models.Fruit.id).join(models.ChargeType).filter(models.ChargeType.id == int(key)).all()[0][0]
     7 IndexError: list index out of range
     8 
     9 File "/home/senguo/KuaiPan/Senguo/gitwork/senguo.cc/admin/handlers/admin.py", line 717, in post
    10 fruit_id = self.session.query(models.Fruit.id).join(models.ChargeType).filter(models.ChargeType.id == int(key)).all()[0][0]
    11 IndexError: list index out of range

        问题所在:没有对自定义商品进行处理的问题。

      6、 严重问题:现在从订单的fruits字段查询店铺的销售情况,在数据量较大的时候这样的方式查询效率非常低,用户体验非常差。
          解决方案:
        (1)千方百计优化目前算法。
        (2)在数据库中为每一个店铺新建一张销售情况表,该表保存了该店铺每一笔订单的每种商品的销售情况。从而在查询销售情况的时候可以直接从这个         表中查询,大大提高查询效率。
        (3)在order表中新增加销售情况字段,每次在订单完成后就往该字段中加入这个订单中每种商品的如下信息,:(商品类目id1:{{商品id1:销售额,商品id2:           销售额,...},该类目销售额},商品类目id2:{{商品id1:销售额,商品id2:销售额,...},该类目销售额},...)  (存为字符串格式)

      7、 admin-sell-count添加新功能:点击一个按钮的时候会出来图表的全屏浏览大图(需要遮罩层技术)。
        问题:把图表放在遮罩层中,显示不出来图表线条。

      8、 admin-sell-count在店铺还没有商品的情况下会出错。

      9、 第三个表要加上图例

      10、 第一个表的tooltip要加上每种类目包含的商品名称
        解决方案:从后台返回每一种类目包含的商品名称,把包含的所有商品连成一个字符串,然后把所有类目的数据以字符串数组的形式返回给前台.前            台把数据加入到series[1]的data数组中,然后tooltip的formatter函数读取该数组内容并显示出来:

     1 options.tooltip.formatter = 
     2   function(params,ticket,callback){
     3     var res = "单类目销售情况<br/>" + params[0].name + ":";
     4     res += params[0].data + "<br/>";
     5     res += "包含商品:" + params[1].data;
     6 
     7     setTimeout(function (){
     8       // 仅为了模拟异步回调
     9       callback(ticket, res);
    10     }, 0)
    11   };

      11、 今日备份:快盘,github.  

    [7.23 周四]

      1、 今日任务:深度优化销售统计算法。

      2、 echarts的tooltip的formatter(内容格式器)属性中可以加入函数。
        用法举例:

     1 formatter:function (params,ticket,callback){
     2   var res = "单类目销售情况<br/>" + params[0].name + ":";
     3   res += params[0].data;
     4 
     5   setTimeout(function (){
     6     // 仅为了模拟异步回调
     7     callback(ticket, res);
     8   }, 0)
     9   // return 'loading';
    10 }
    11 //注:其中每个params[i]对应一个series中的object

      3、 js取字符串的子串:

    1 str = str.substr(start,lenth)

      4、 Echarts legend覆盖图表问题:
        echarts的legend选项值过多的时候会覆盖到下面的图表,这个问题最后这样解决:
        在循环中控制给legend的data赋值的个数,当超过这个个数的时候,就给data赋值空字符串即可.

      5、 店铺后台销售统计bug:当店铺还没有商品的时候前台和后台都报错.
        解决方法:js第一次发post请求,后台先查询该店铺的商品列表是不是为空,如果为空就直接返回前台一个标记,前台在后面的所有post请求都没必要          发了,直接在页面显示该店铺没有商品即可.

      6、 现在m_goods及相关的几张表都弃用了,所有商品都存到了fruits表中.之前order表有一个fruits字段和一个mgoods字段,现在mgoods字段也弃用了,     只用fruits字段.

      7、 dbinitdata.py文件中有所有水果的信息.

      8、 问题:echarts放在新的图层上,新图层初始化为hidden,为一个按钮绑定点击事件,点击的时候显示这个图层,但点击按钮后发现只能显示坐标轴,     数据并不难显示出来.
        解决方法:把新图层div初始化隐藏的方式改为:.invisiable{visibility: hidden;}即可.

      9、 居中问题:
        div居中:
          margin: 0 auto;
        text居中:
          text-align:center;

      10、 销售统计添加遮罩层显示大图功能成功!剩下的就是各种性能优化,以及对于用户自定义商品的错误处理的修正.

      11、 把除水果和干果以外的"其他"商品都当做"其他"处理.

      12、 在js中代码任意处加"debugger;",则可以进行断点调试,而且调试的时候变量的值也会自动显示处理,这个很厉害.

      13、 chrome浏览器模拟手机:打开小手机的图标,然后在上方的UA栏中输入MicroMessenger即可.

      14、 新任务:店铺后台的统计项目(前30)
            统计同种类目的销售额前十名店铺

      15、 今日备份:快盘,github.  

    [7.24 周五]
      1、 今日任务:
        优化店铺后台的销售统计
        总后台的销售统计
      2、 python计算一段程序的执行时间:

    1 import time
    2 start = time.clock()
    3 #这里是要测试的代码段
    4 ...
    5 end = time.clock()
    6 print("所用时间为:",end-start,"")
    7 #注:时间单位为秒,可以转化为毫秒(python的时间戳单位也是毫秒):
    8 print(("所用时间为:%.3fms") % ((end - start)/1000))

      3、 经过断点测试每一段代码的执行时间,发现店铺后台销售统计的程序执行时间主要花在了处理订单中的fruits字段并将fruits字段的数据转化成单种商品的销售额这一块了,数据量稍大的时候这一部分的执行时间占了总执行时间的90%以上,所以主要在这里进行性能优化.
        优化方案:
        (1)经过分析发现,消耗时间较多的代码段主要是在两个方面消耗时间:
          多重循环和多重循环中的数据库查询操作.
          因此考虑将每次循环中相同或重复的数据库查询操作在循环开始之前就查好并把结果存在列表里面,等到要查的时候直接在列表中找,这样可以大大优化效率.
          主要的可优化查询操作有:
          ①select id from charge_type where fruit_id in (select id from fruit where shop_id = self.current_shop.id); --->查询当前店铺的所有商品的计价方式的id,并存放在列表中:shop_charge_type_id_list
          ②然后建立一个字典,该字典的键表示该店铺的所有计价方式id,值表示每种计价方式id对应的fruit_id
    select fruit_id from charge_type where id in (select id from charge_type where fruit_id in (select id from fruit where shop_id = 4));
            即:select fruit_id from charge_type where id in (select id from charge_type where fruit_id in shop_charge_type_id_list;
          ③再遍历上一步的字典的每个fruit_id,根据fruit_id从数据库中查出对应的所有的fruit_name,并添加到fruit_id的后面,与fruit_id构成一个子字典.
          ④每次在循环中的查询都可以优化为判断每个order表的fruits字段的计价方式是否在(3)中字典的键列表中,如果在则再查对应的fruit_id和fruit_name,这样可以初步大大提高效率.
          ⑤对于之前order表的mgoods字段的商品,这样处理:
            先根据menu表和m_goods表的联合查询,查询当前店铺所有的自定义商品的id和name,并保存到:shop_mgoods_list表中:
              select m_goods.id,m_goods.name from m_goods,menu where m_goods.menu_id = menu.id and menu.shop_id = self.current_shop.id;
            然后遍历shop_mgoods_list表,查询m_charge_type表中所有和shop_mgoods_list表中m_goods_id相同的计价方式id,并存在一个表中:shop_mgoods_ charge_type;
          ⑥最后建立一个字典列表,每个字典以上述第二步的计价方式id为键,以mgoods_id和mgoods_name组成的列表为值.
            每次在循环中的查询都可以优化为判断每个order表的mgoods字段的计价方式在上面字典中对应的mgoods_id和mgoods_name,这样也可以初步大大提高效率.

      4、 python中合并两个字典的方法(假设a,b是两个字典):
        方法1:c = dict(a,**b)
        方法2:c = a.copy()
           c.update(b)
        从效率上来讲方法2的效率更高.

      5、 python获取字典的键列表(假设d为一个字典):
          key_list = list(d.keys())
        获取字典的值的列表:
          value_list = list(d.values())
        获取字典的键和值组成的元组的列表:
          item_list = list(d.items())

      6、 python以下代码并不会改变列表a:

    1 a = [1,2,3,4,5]
    2 for e in a:
    3     e = e+1

        注:但用sqlalchemy对数据库进行查询并且做修改的时候,这种方法是能改变原来列表的。这一点要特别注意。

      7、 店铺后台销售统计前两个表的左边距太小.

      8、 今日数据备份:快盘,github.

    [7.25 周六]

      1、 今日任务:整个森果系统的余额错误对账
      2、 昨天陈明遇到一个问题:在数据库中加表以后,推到线上测试的时候因为数据库的编码问题导致线上git分支不能用了,解决方法是:
        在admin.py顶部加入:

    1 import codecs 
    2 codecs.register(lambda name: codecs.lookup(‘utf8’) if name == ‘utf8mb4’ else None)

      3、 上午完成了店铺后台的销售统计的优化,大大提高了查询速度,将原来数据量大的时候的5秒左右的执行时间优化到了300ms左右,提高了用户体验.
        还有一个问题:每个销售统计图的后台重复代码略多,可以考虑进行合并和封装.

      4、 今日任务数据库分析:
        (1)shop表:
          *shop_balance字段:当前店铺的余额
          *available_balance:该店铺当前的可提现余额
          *正确的情况下:同一时刻available_balance <= shop_balance
          *mysql> select id from shop where shop_code = 'czneau';
          +----+
          | id |
          +----+
          | 4 |
          +----+

          注:“吃在东农”店铺的数据量比较大,为了数据统计的方便性,以该店铺为例.

        (2)order表:
          *pay_type = Column(TINYINT, default=1)#付款方式:1:货到付款,2:余额 3:在线支付
        (3)balancehistory表:
          *balance_type = Column(Integer,default = 1) # 0:代表充值 ,1:余额消费 2:提现 3:在线支付 4:商家删除订单 5:用户自己取消订单 6:余额消费完成 ,可提现额度的变化
          *这个表是从2015-5-3开始有记录的。
        (4)customer_shop_follow表:
          *shop_balance:该用户在该店铺存的余额

      5、 python中保留小数位数的两种方法:
          round(1.2222,2),结果为1.22,类型为float
          format(1.2222,".2f"),结果为'1.22',类型为str

      6、 当然果园:id = 1163;果缤纷:id = 1080

      7、 sqlalchemy的几种查询结果的形式总结:
        (1)self.session.query(models.Shop).filter(...).count()
          查询结果是一个整数,表示满足条件的结果的条数.
        (2)self.session.query(models.Shop).filter(...).all()
          查询结果是一个list,每一个元素都是<class 'dal.models.Shop'>类型的对象.如果查询结果为空,则返回一个空的list.
        (3)self.session.query(models.Shop).filter(...).first()
          查询结果是一个<class 'dal.models.Shop'>类型的对象.如果查询结果为空,则返回一个<class 'NoneType'>类型的对象.
        (4)self.session.query(models.Shop.id,models.Shop.shop_name,...).filter(...).all()
          查询结果是一个list,每一个元素都是一个元组,该元组包含了id,shop_name,...这些信息.如果查询结果为空,则返回一个空的列表.
        (5)self.session.query(models.Shop.id,models.Shop.shop_name,...).filter(...).first()
          查询结果是一个元组,类型为<class 'sqlalchemy.util._collections.result'>,该元组包含了id,shop_name,...这些信息.如果查询结果为空,则返回一个None.
        (6)self.session.query(models.Shop).filter(...)
          查询结果是一个<class 'sqlalchemy.orm.query.Query'>类型的数据,具体内容是一个mysql查询语句,类似于这样:

    1 SELECT shop.id AS shop_id 
    2 FROM shop 
    3 WHERE shop.shop_status = %(shop_status_1)s

      8、 数据查询:

     1     select shop.shop_name,balancehistory.create_time,shop.shop_balance,shop.available_balance,balance_record,balancehistory.balance_value, balancehistory.available_balance,balance_type from balancehistory,shop where shop.id = balancehistory.shop_id and shop.id = 1080 order  by balancehistory.create_time;
     2 
     3     select senguocc.order.id,senguocc.order.shop_id,senguocc.order.create_date,senguocc.order.totalPrice,senguocc.order.pay_type from senguocc.order where shop_id = 235;
     4 
     5     select create_time,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where shop_id = 1163;
     6     select create_time,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where shop_id = 4 order by create_time;
     7     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type = 0 order by shop_id,create_time;
     8     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type = 1 order by shop_id,create_time;
     9     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type = 2 order by shop_id,create_time;
    10     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type = 3 order by shop_id,create_time;
    11     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type = 4 order by shop_id,create_time;
    12     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type = 5 order by shop_id,create_time;
    13     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type = 6 order by shop_id,create_time;
    14     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type = 7 order by shop_id,create_time;
    15 
    16     select create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type in (4,5) order by shop_id,create_time;
    17 
    18     select senguocc.order.create_date,senguocc.order.pay_type,senguocc.order.num,senguocc.order.totalPrice,senguocc.order.today, senguocc.order.arrival_time from senguocc.order where senguocc.order.shop_id = 4 and senguocc.order.status >= 5 and  date_format(senguocc.order.create_date,"%Y-%m-%d")>"2015-05-02" order by senguocc.order.create_date;
    19 
    20 
    21 
    22     select senguocc.order.create_date,senguocc.order.pay_type,senguocc.order.num,senguocc.order.totalPrice,senguocc.order.today, senguocc.order.arrival_time from senguocc.order where senguocc.order.shop_id = 4 and senguocc.order.status >= 5 and 
    23  date_format(senguocc.order.create_date,"%Y-%m-%d")>"2015-05-02" order by senguocc.order.create_date;
    24 
    25     select senguocc.order.create_date,senguocc.order.pay_type,senguocc.order.num,senguocc.order.totalPrice,senguocc.order.today, senguocc.order.arrival_time from senguocc.order where senguocc.order.shop_id = 4 and senguocc.order.status >= 5 and 
    26  senguocc.order.pay_type in (2,3) and date_format(senguocc.order.create_date,"%Y-%m-%d")>"2015-05-02" order by senguocc.order.create_date;
    27 
    28     select create_time,balance_record from balancehistory where balance_type in (1,3) order by create_time;

      9、 order表pay_type每个值表示的意思:
        1:货到付款,2:余额 3:在线支付

      10、 店铺shop_totalPrice分析:
        balance_type的每个值表示的意思:
        [tee /home/senguo/log.txt](这个语句用于将mysql的输出结果存储到一个文本文档里,在数据量较大的时候方便分析)
        0:用户充值;1:余额消费;2:商家提现;3:在线支付(未完成);4:商家删除订单;5:用户自己取消订单;6:余额消费完成;7:在线支付完成
        (1)shop表的shop_balance的值和balancehistory表的该店铺对应的按时间升序排序的记录的最后一条记录的shop_totalPrice的值应该是一致的.
        (2)对于一个特定的shop_id,balancehistory表的该店铺对应的按时间升序排序的记录中,当balance_type in [0,1,3]时,应该有:shop_totalPrice = shop_totalPrice' + balance_value,其中shop_totalPrice表示当前记录中的shop_totalPrice,shop_totalPrice'表示离当前最近的一次的相同的操作记录的shop_totalPrice.同时available_balance不应该发生变化.
        (3)对于一个特定的shop_id,balancehistory表的该店铺对应的按时间升序排序的lx记录中,当balance_type = 2时,应该有:shop_totalPrice = shop_totalPrice' - balance_value同时available_balance = available_balance' - balance_value,其中shop_totalPrice表示当前记录中的shop_totalPrice,shop_totalPrice'表示离当前最近的一次的操作记录的shop_totalPrice.available_balance表示当前记录中的shop_totalPrice,available_balance'表示离当前最近的一次的提现或者可提现额度入账的操作记录(balance_type in [2,6,7])的available_balance. 
        (4)对于一个特定的shop_id,balancehistory表的该店铺对应的按时间升序排序的记录中,当balance_type in [4,5]时,应该有:shop_totalPrice = shop_totalPrice' - balance_value,其中shop_totalPrice表示当前记录中的shop_totalPrice,shop_totalPrice'表示离当前最近的一次的balance_type in [0,1,3]的操作记录的shop_totalPrice.同时available_balance不应该发生变化.

      11、 店铺available_balance分析:
        (1)对于一个特定的shop_id,balancehistory表的该店铺对应的按时间升序排序的记录中,当balance_type in [6,7]时,available_balance = available_balance' + balance_value,其中available_balance表示当前记录中的shop_totalPrice,available_balance'表示离当前最近的一次的相同的操作记录的available_balance.同时shop_totalPrice不应该发生变化.
        (2)对于一个特定的shop_id,balancehistory表的该店铺对应的按时间升序排序的记录中,当balance_type = 2时,应该有:shop_totalPrice = shop_totalPrice' - balance_value同时available_balance = available_balance' - balance_value,其中shop_totalPrice表示当前记录中的shop_totalPrice,shop_totalPrice'表示离当前最近的一次的操作记录的shop_totalPrice.available_balance表示当前记录中的shop_totalPrice,available_balance'表示离当前最近的一次的提现或者可提现额度入账的操作记录(balance_type in [2,6,7])的available_balance.

      12、 balance_type分析:
        (1)能导致shop_totalPrice增大的balance_type有:[0,1,3];能导致shop_totalPrice减小的balance_type有:[2,4,5];shop_totalPrice不变的balance_type有:[6,7]
        (2)能导致available_balance增大的balance_type有:[6,7];能导致available_balance减小的balance_type有:[2];available_balance不变的balance_type有:[0,1,3,4,5].

      13、 总额分析:
        (1)对于某一个店铺的balancehistory,在按照时间升序排列的记录中,所有balance_type in [6,7]的记录的balance_value加起来并减去所有balance_type=2的记录的balance_value,结果应该等于店铺当前最新的available_balance.
        (2)同一时刻应该有available_balance <= shop_balanc
        (3)因为有些订单因为并发等原因并没有记录到balancehistory表中,所以对账的时候要以order表为依据.

      14、 发现的问题整理:
        (1)shop_id = 1163
    +---------------------+--------------+------------------------------------------------+---------------+-----------------+-------------------+
    | create_time | balance_type | balance_record | balance_value | shop_totalPrice | available_balance |
    +---------------------+--------------+------------------------------------------------+---------------+-----------------+-------------------+
    | 2015-06-05 16:31:45 | 7 | 可提现额度入账:订单1163000001完成 | 2.5 | 2.5 | 2.5 |
    | 2015-06-05 16:32:31 | 3 | 在线支付(支付宝):订单1163000002 | 6.5 | 9 | 0 |
        

        (2)shop_id = 1163
    | 2015-06-07 13:49:57 | 3 | 在线支付(微信):订单1163000132 | 6.5 | 1051.6 | 0 |
    | 2015-06-07 14:00:51 | 7 | 可提现额度入账:订单1163000118完成 | 9 | 1051.6 | 11.5 |


        (3)shop_id = 1163
    | 2015-06-07 14:05:24 | 7 | 可提现额度入账:订单1163000084完成 | 7.5 | 1051.6 | 993.1 |
    | 2015-06-07 14:11:53 | 3 | 在线支付(支付宝):订单1163000134 | 14 | 1065.6 | 0 |


        (4)
    | 2015-06-07 14:05:24 | 7 | 可提现额度入账:订单1163000084完成 | 7.5 | 1051.6 | 993.1 |
    | 2015-06-07 14:11:53 | 3 | 在线支付(支付宝):订单1163000134 | 14 | 1065.6 | 0 |


        (5)
    | 2015-06-07 18:55:21 | 3 | 在线支付(支付宝):订单1163000158 | 14.3 | 1333.5 | 0 |
    | 2015-06-07 19:04:28 | 7 | 可提现额度入账:订单1163000153完成 | 20.3 | 1333.5 | 1013.4 |


        (6)
    | 2015-06-08 09:45:36 | 3 | 在线支付(支付宝):订单1163000255 | 2.5 | 2254.7 | 0 |
    | 2015-06-08 09:46:04 | 2 | 提现:管理员 欧先森•当然果园 | 1350 | 904.7 | 29.9 |
    | 2015-06-08 09:46:57 | 3 | 在线支付(微信):订单1163000259 | 16 | 920.7 | 0 |


        (7)
    | 2015-06-08 19:31:59 | 3 | 在线支付(微信):订单1163000451 | 6 | 2594.6 | 0 |
    | 2015-06-08 19:34:01 | 7 | 可提现额度入账:订单1163000331完成 | 3 | 2594.6 | 32.9 |


        (8)
    | 2015-06-09 10:58:55 | 3 | 在线支付(支付宝):订单1163000597 | 13.8 | 3966.4 | 0 |
    | 2015-06-09 10:59:23 | 2 | 提现:管理员 欧先森•当然果园 | 2600 | 1366.4 | 64.9 |
    | 2015-06-09 11:03:34 | 3 | 在线支付(微信):订单1163000599 lx | 15 | 1381.4 | 0 |


        (9)
    | 2015-06-09 22:08:48 | 3 | 在线支付(微信):订单1163000719 | 5.8 | 2531.1 | 0 |
    | 2015-06-09 22:32:53 | 2 | 提现:管理员 欧先森•当然果园 | 2400 | 131.1 | 64.1 |
    | 2015-06-09 22:36:34 | 3 | 在线支付(支付宝):订单1163000720 | 13.3 | 144.4 | 0 |
        注:以上问题是因为在生成记录的时候根本就没有记录当前操作不会改变的项的值,只要保证离当前最近的一次的相同的操作的该项的值正确就可以.这一点不会造成数据错误.

      15、 订单号组成:前若干位是店铺id,后6位是该店铺的订单的累计个数.

      16、 在以下c语言代码段中只能修改或增加或删除一个字符,使得输出结果是20个'-',给出三种方法(今天陈明不知道从哪找的一个题)
        代码段:

    1 #include <stdio.h>
    2 int main()
    3 {
    4     int n = 20;
    5     int i;
    6     for(i = 0; i < n;i--){
    7         printf("-");    
    8     }
    9 }    
     1 //方法1:
     2 #include <stdio.h>
     3 int main()
     4 {
     5     int n = 20;
     6     int i;
     7     for(i = 0; i + n;i--){
     8         printf("-");    
     9     }
    10 }
    11 
    12 //方法2:
    13 #include <stdio.h>
    14 int main()
    15 {
    16     int n = 20;
    17     int i;
    18     for(i = 0; i < n;n--){
    19         printf("-");    
    20     }
    21 }
    22 
    23 //方法3:
    24 #include <stdio.h>
    25 int main()
    26 {
    27     int n = 20;
    28     int i;
    29     for(i = 0; -i < n;i--){
    30         printf("-");    
    31     }
    32 }            

      17、 sqlalchemy查询中,filter和filter_by的区别:
        (1)filter:
          self.session.query(models.Shop.shop_name).filter(models.Shop.id == 888).all()
        (2)filter_by:
          self.session.query(models.Shop.shop_name).filter(id = 888).all()
        结论:还是filter_by的写法比较简单,以后如果只判断相等关系都用filter_by.如果判断其他的如不等关系、in关系等,还是要用filter.

      18、 sqlalchemy如果某一个表类的某一个字段和另外一张表有relationship关系,那么可以直接引用该relationship字段的子字段,比如在order表中有如下shop字段:

    1 class Order(MapBase, _CommonApi):
    2       ...
    3       shop = relationship("Shop", uselist=False,join_depth=1)
    4       ...

        那么可以直接这样写:

    1 shop_order = self.session.query(models.Order).filter_by(shop_id = 4).first()
    2 print(shop_order.shop.shop_name)

        输出结果是id = 4的店铺的名称.

       19、 今日数据:快盘,github.

    [7.27 周一]

      1、 今日任务:余额错误对账
      2、 提取字符串中第若干位到倒数第三位之间的数字子串:

    1 str0 = "可提现额度入账:订单1234002379121完成"
    2 i = -1
    3 while i < len(str0):
    4 i += 1
    5 if str0[i].isdigit():
    6 break
    7 print(str0[-(len(str0) - i) : -2])

        输出结果:1234002379121

      3、 查找一下balancehistory表中最早是从什么开始将shop_totalPrice和available_balance分离的(即在balance_record字段中balance_type in (6,7)第一次出现的记录).
        select create_time,balance_type,balance_record,balance_value,shop_totalPrice,available_balance from balancehistory where balance_type in (6,7) order by create_time;
        查询结果第一条为:
    +---------------------+--------------+------------------------------------------------+---------------+-----------------+-------------------+
    | create_time | balance_type | balance_record | balance_value | shop_totalPrice | available_balance |
    +---------------------+--------------+------------------------------------------------+---------------+-----------------+-------------------+
    | 2015-05-09 11:35:00 | 6 | 可提现额度入账:订单607000133完成 | 27 | 173 | 109 |

        查询所有用余额支付或在线支付方式的订单的信息:
    select senguocc.order.create_date,senguocc.order.pay_type,senguocc.order.num,senguocc.order.totalPrice,senguocc.order.today,senguocc.order.arrival_time from senguocc.order where senguocc.order.status >= 5 and senguocc.order.pay_type in (2,3) and date_format(senguocc.order.create_date,"%Y-%m-%d")>"2015-05-02" order by senguocc.order.create_date;
        从查询结果中查订单号为的订单信息是:
    +---------------------+----------+------------+------------+-------+--------------+
    | create_date | pay_type | num | totalPrice | today | arrival_time |
    +---------------------+----------+------------+------------+-------+--------------+
    | 2015-05-08 12:06:55 | 2 | 607000133 | 27 | 1 | 11:35 |

      4、 查错思路:
        *范围一:所有余额支付和在线支付(还未支付完成)的订单
        (1)先查询balancehistory表所有balance_type in (1,3)的balance_record字段,按时间升序排列,分离balance_record字段中的订单号,并存到一个list balance_type_1_3中.
        (2)然后查询order表中所有pay_type in (2,3)并且status >= 5的shop_id和num字段,按时间升序排列,将每一组shop_id和num字段组成一个子列表,并存到一个list pay_type_2_3中.
        (3)循环遍历pay_type_2_3,每次循环查找pay_type_2_3的元素的第二项(订单编号)是否在表balance_type_1_3中,如果不在,输出该元素的第一项和第二项.


        *范围二:所有余额支付和在线支付(支付完成)的订单
        (1)先查询balancehistory表所有balance_type in (6,7)的balance_record字段,按时间升序排列,分离balance_record字段中的订单号,并存到一个列表listbalance_type_6_7中. 
        (2)然后查询order表中所有pay_type in (2,3)并且status >= 5的shop_id和num字段,按时间升序排列,将每一组shop_id和num字段组成一个子列表,并存到一个list pay_type_2_3中.
        (3)循环遍历pay_type_2_3,每次循环查找pay_type_2_3的元素的第二项(订单编号)是否在表list balance_type_6_7中,如果不在,输出该元素的第一项和第二项.

      5、 所有订单表中有的余额和在线支付(未完成)记录要插入到balancehistory表中,时间用订单的create_date;
        所有订单表中有的余额和在线支付完成的记录要插到balancehistory表中,时间用订单的arrival_day和arrival_time合起来的时间.
        注:插入时候balancehistory的id还是按照自动增长的方式,依次排列.

      6、 balancehistory在5.14和5.15两天有八条balance_record异常的记录,要通过时间查询订单号,然后修正过来.

        select create_time,id,customer_id,balance_record,balance_value from balancehistory where balance_type in (1,3) order by create_time;
        select create_date,num,customer_id,shop_id,arrival_day,arrival_time,totalPrice from senguocc.order where date_format(create_date,"%Y-%m-%d %H") like "2015-05-15 12" and customer_id = 33111;
    +---------------------+-------+-------------+--------------------------------------------+---------------+
    | create_time | id | customer_id | balance_record | balance_value |
    +---------------------+-------+-------------+--------------------------------------------+---------------+
    | 2015-05-14 23:56:44 | 698 | 1 | 在线支付(微信):用户 黄铁森 | 0.01 |
    | 2015-05-15 00:29:07 | 699 | 16435 | 在线支付(微信):用户 托物言志 | 14 |
    | 2015-05-15 01:25:44 | 700 | 1854 | 在线支付(微信):用户 Woody | 4.46 |
    | 2015-05-15 01:28:44 | 701 | 1 | 在线支付(微信):用户 黄铁森 | 0.01 |
    | 2015-05-15 09:01:27 | 719 | 29452 | 在线支付(微信):用户 美然 | 8.98 |
    | 2015-05-15 12:29:18 | 728 | 32843 | 在线支付(微信):用户 96line7 | 13 |
    | 2015-05-15 12:38:14 | 729 | 10788 | 在线支付(微信):用户 冯艺 | 22.5 |
    | 2015-05-15 12:43:06 | 731 | 33111 | 在线支付(微信):用户 张淑娜。 | 10 |

    +---------------------+-----------+-------------+---------+-------------+--------------+------------+
    | create_date | num | customer_id | shop_id | arrival_day | arrival_time | totalPrice |
    +---------------------+-----------+-------------+---------+-------------+--------------+------------+
    | 2015-05-14 23:56:23 | 197000176 | 1 | 197 | NULL | NULL | 0.01 |
    | 2015-05-15 00:27:36 | 615000311 | 16435 | 615 | 2015-05-15 | 18:59 | 14 |
    | 2015-05-15 01:24:33 | 271000022 | 1854 | 271 | 2015-05-15 | 11:39 | 4.46 |
    | 2015-05-15 01:28:24 | 197000177 | 1 | 197 | NULL | NULL | 0.01 |
    | 2015-05-15 09:00:29 | 624001065 | 29452 | 624 | 2015-05-16 | 22:19 | 8.98 |
    | 2015-05-15 12:29:06 | 848000584 | 32843 | 848 | 2015-05-15 | 19:01 | 13 |
    | 2015-05-15 12:37:51 | 624001076 | 10788 | 624 | 2015-05-16 | 22:20 | 22.5 |
    | 2015-05-15 12:42:41 | 848000585 | 33111 | 848 | 2015-05-15 | 19:02 | 10 |
    +---------------------+-----------+-------------+---------+-------------+--------------+------------+

        select num from senguocc.order where date_format(create_date,"%Y-%m-%d %H-%M-%S") like "2015-05-14 12:36:00";
        问题:因为订单的create_date和balancehistory的create_time字段存在几秒到几分钟的时间延迟,所以通过时间反查订单号的方法行不通.
        解决方案:手动修改这八条记录.

      7、 Mysql中如何创建一个表让id自动增长?

    1 CREATE TABLE users (
    2     id int(5) NOT null auto_increment,
    3     name varchar(20)NOT null,
    4     PRIMARY KEY (`id`)
    5 )

        举例:

    1 create table test_autoid (id int(11) not null primary key auto_increment,name char(5) not null);
    2 insert into test_autoid(name) values('jyj');
    3 ...(省略了八条插入)
    4 select * from test_autoid;

    +----+------+
    | id | name |
    +----+------+
    | 1 | jyj |
    | 2 | aa |
    | 3 | bb |
    | 4 | cc |
    | 5 | d |
    | 6 | eee |
    | 7 | ff |
    | 8 | gg |
    | 9 | hh |
    +----+------+

    1 delete from test_autoid where id = 3;
    2 select * from test_autoid;

    +----+------+
    | id | name |
    +----+------+
    | 1 | jyj |
    | 2 | aa |
    | 4 | cc |
    | 5 | d |
    | 6 | eee |
    | 7 | ff |
    | 8 | gg |
    | 9 | hh |
    +----+------+

    1 insert into test_autoid(name) values('new');
    2 select * from test_autoid;

    +----+------+
    | id | name |
    +----+------+
    | 1 | jyj |
    | 2 | aa |
    | 4 | cc |
    | 5 | d |
    | 6 | eee |
    | 7 | ff |
    | 8 | gg |
    | 9 | hh |
    | 10 | new |
    +----+------+

      8、 改错步骤:
        mysql> select balance_record from balancehistory where balance_type = 3 and (balance_record not like '在线支付(微信):订单%' and balance_record not like '在线支付(支付宝):订单%');
    +--------------------------------------------+
    | balance_record |
    +--------------------------------------------+
    | 在线支付(微信):用户 黄铁森 |
    | 在线支付(微信):用户 托物言志 |
    | 在线支付(微信):用户 Woody |
    | 在线支付(微信):用户 黄铁森 |
    | 在线支付(微信):用户 美然 |
    | 在线支付(微信):用户 96line7 |
    | 在线支付(微信):用户 冯艺 |
    | 在线支付(微信):用户 张淑娜。 |
    +--------------------------------------------+
        select id,create_time,shop_id,balance_type,balance_record,balance_value,customer_id,customer_totalPrice,shop_totalPrice,available_balance, is_cancel from balancehistory where shop_id = 197 order by create_time;

        (1)先手动将balancehistory的八条异常记录修正:在线支付(微信):订单% 在线支付(支付宝):订单

    1 update balancehistory set balance_record="在线支付(微信):订单197000176" where id = 698;
    2 update balancehistory set balance_record="在线支付(微信):订单615000311" where id = 699;
    3 update balancehistory set balance_record="在线支付(微信):订单271000022" where id = 700;
    4 update balancehistory set balance_record="在线支付(微信):订单197000177" where id = 701; 
    5 update balancehistory set balance_record="在线支付(微信):订单624001065" where id = 719;
    6 update balancehistory set balance_record="在线支付(微信):订单848000584" where id = 728;
    7 update balancehistory set balance_record="在线支付(微信):订单624001076" where id = 729;
    8 update balancehistory set balance_record="在线支付(微信):订单848000585" where id = 731;

        临时记录:'197000176','615000311','271000022','197000177','624001065','848000584','624001076','848000585'
    698,699,700,701,719,728,729,731

        (2)删除shop_id = 0的记录。
          delete from balancehistory where shop_id = 0 or id = 19088;
        (3)查询balancehistory表所有balance_type in (1,3)的balance_record字段,按时间升序排列,分离balance_record字段中的订单号,并存到一个list balance_type_1_3中:

    1 balance_type_1_3 = []
    2 query_list = self.session.query(models.BalanceHistory.create_time,models.BalanceHistory.balance_record).filter(models.BalanceHistory.balance_type.in_([1,3])).all()
    3 for item in query_list:
    4     for i in range(len(item[1])):
    5     if item[1][i].isdigit():
    6         break
    7     balance_type_1_3.append(item[1][i : len(item[1])])    

        (4)查询order表中所有pay_type in (2,3)并且status >= 5的shop_id和num字段,按时间升序排列,将每一组shop_id和num字段组成一个子列表,并存到一个list pay_type_2_3中;

        *注意事项:因为balancehistory中每一条记录与其他记录的店铺余额和可提现余额都是相互关联的,所以为了避免混乱,首先只需把缺少的不包括这两项的记录插到表中,然后再依据其他信息依次推出这两项的值。


      9、 python的format的用法:

    1 >>>"i am {0} years old,i am now in {1}".format(22,'wuhan')
    2 'i am 22 years old,i am now in wuhan'

      10、 今日数据:快盘,github.

    [7.28 周二]

      1、 今日任务:余额错误对账
      2、 查询每个店铺的当前shop表中的shop_balance,available_balance两个值是否与balancehistory表中该店铺最后一条记录的两者的值是否相吻合.

    1 select id,shop_id,create_time,shop_totalPrice,available_balance from balancehistory where balance_type in(0,1,2,3,4,5) order by shop_id,create_time desc;
    2 select shop_id,shop_totalPrice,available_balance from balancehistory where balance_type in(2,6,7) group by shop_id order by create_time desc limit 0,1;

        注:因为分组查询再取limit会出现错误,即分组后查询后再取每组的第一个值的问题没有解决,所以分组查询后在python中进行处理取最新一条记录的值。


      3、 mysql取分组后每组最后一条数据的一个方法:

     1 create table test_group(id0 int(5) primary key,id int,name char(5));
     2 ...(几个insert语句)
     3 select * from test_group;
     4 +-----+------+------+
     5 | id0 | id | name |
     6 +-----+------+------+
     7 | 1 | 100 | aa |
     8 | 2 | 100 | bb |
     9 | 3 | 100 | cc |
    10 | 4 | 101 | dd |
    11 | 5 | 101 | ee |
    12 | 6 | 102 | ff |
    13 | 7 | 102 | gg |
    14 | 8 | 102 | hh |
    15 | 9 | 102 | ii |
    16 | 10 | 103 | jj |
    17 +-----+------+------+
    18 select id0,name from test_group X where id0 in (select max(id0) from test_group Y group by (id));
    19 +-----+------+
    20 | id0 | name |
    21 +-----+------+
    22 | 3 | cc |
    23 | 5 | ee |
    24 | 9 | ii |
    25 | 10 | jj |
    26 +-----+------+

      4、 mysql子查询不支持limit问题解决
        This version of MySQL doesn’t yet support ‘LIMIT & IN/ALL/ANY/SOME 错误解决
        在一个Mysql表达式中使用嵌套查询,出现了这个错误。原因是内层select语句带有limit子句。
        在网上查了下,有文章指出:
        比如这样的语句是不能正确执行的。

    1 select * from table where id in (select id from table limit 12);

        但是,只要你再加一层就行。如:

    1 select * from table where id in (select t.id from (select * from table limit 12)as t)

        这样就可以绕开limit子查询的问题。
        问题解决。
        后来我发现,上述是解决问题的一个方法,其实还有一个更好的做法,就是把限制条件放到from而非where子句中,就不必出现嵌套再嵌套。
    如上例,可以改为:

    1 select * from (select id from table limit 12) as foo;

        注意:其实as foo特别重要,如果不写成from () as xxx的形式,即不给from后的select语句构成表名,那么最后系统仍会报错。

      5、 balancehistory表中balance_type in (4,5)的记录表示的是商家删除订单或者用户取消订单的记录,但这样的记录的订单号在order表中对应的status都为0,所以如果order表中有遗漏的status=0的记录没有插入到balancehistory表中,那么插入的时候都按照balance_type=5进行插入。

        #用户在线支付成功的订单不能取消也不能删除,但余额支付的订单可以。
        #余额支付的订单不会有“未付款”的状态,因为在提交订单的同时会进行余额支付操作,而在线支付提交订单并不会同时支付,而是还要进入支付页面进行支付。
        #经过检测,发现不存在balancehistory中店铺以外的店铺没有插入相关记录的情况,因此只考虑balancehistory表中的店铺的错漏,所以shop_list_query2可以先不考虑。
        #order表中online_type字段表示在线支付的类型('wx'或'alipay')
        #设定一个change_shop_id列表,把balancehistory表中修改过的店铺的id都存进去.

    1 select create_date,pay_type,totalPrice from senguocc.order where shop_id = 1080 and num = "1080000332";
    2 select num,pay_type,status,totalPrice from senguocc.order where shop_id = 1080 and customer_id = 48338;
    3 select create_time,shop_id,balance_type,balance_record,balance_ value, shop_totalPrice,available_balance from balancehistory where shop_id = 1080 order by create_time;

      6、 sqlalchemy修改数据库中某一个表的某些字段的值的方法:

    query = self.session.query(models.Order).filter_by(num = "1203000000").first()
    query.pay_type = 22
    self.session.commit()

      7、 sqlalchemy向表中添加记录的方法:

    1 balance_history = models.BalanceHistory(customer_id = insert_list[i][0],
    2 shop_id = insert_list[i][1] ,name = insert_list[i][2],balance_value = insert_list[i][3] ,
    3 balance_record = insert_list[i][4],create_time = insert_list[i][5],balance_type = 1)
    4 self.session.add(balance_history)
    5 self.session.commit()

      8、 sqlalchemy上锁方法:

    1 query = self.session.query(models.Order).filter_by(num = "1203000000").with_lockmode("update").first()

      9、 元组中的元素不能进行自加操作,只能先把元组转化为字符串(用list方法),然后才能执行自加操作

      10、 今日数据:快盘,github.

    [7.30 周四]
      1、 今日任务:余额错误对账

    1 select create_time,shop_id,balance_type,balance_record,balance_ value, shop_totalPrice,available_balance from balancehistory order by shop_id,create_time;
    2 select shop_id,count(*) from balancehistory where count(*) < (select count(*) from senguocc.order where senguocc.order.shop_id = balancehistory.shop_id and senguocc.order.pay_type = 3) group 
    3 by shop_id;
    4 select shop_id,count(*) from balancehistory where balance_type = 3 group by shop_id;

      2、 异常数据:

     1 totalprice: 39 10.4 0
     2 totalprice: 259 66.5 0
     3 available_balance: 271 0.0 8.92
     4 totalprice: 287 16.5 0
     5 totalprice: 444 70.0 0
     6 totalprice: 563 26.4 0
     7 totalprice: 838 1.0 0
     8 totalprice: 1037 10.0 0
     9 available_balance: 1037 10.0 0
    10 totalprice: 1057 1.0 0
    11 available_balance: 1057 0.01 0
    12 totalprice: 1108 2.0 0
    13 totalprice: 1115 11.0 0
    14 totalprice: 1141 3.0 0
    15 totalprice: 1230 4.0 0
    16 totalprice: 1284 1.0 0
    17 totalprice: 1317 0.01 0
    18 totalprice: 1340 1.01 0
    19 totalprice: 1473 38.0 0

      3、 零散后台打印数据记录:

     1 !!@@@11111111111111 197
     2 @@@@ 7
     3 4
     4 ['197000154', '197000155', '197000156']
     5 !!@@@11111111111111 661
     6 @@@@ 2
     7 0
     8 ['661000036', '661000037']
     9 !!@@@11111111111111 848
    10 @@@@ 2
    11 1
    12 ['848000285']
    1 [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 69, 69, 69, 69, 69, 69, 69, 69, 197, 197, 197, 197, 197, 197, 235, 272, 289, 289, 289, 488, 488, 549, 549, 607, 607, 607, 
    2 607, 607, 607, 607, 615, 624, 624, 624, 624, 624, 624, 624, 624, 661, 661, 661, 661, 661, 848, 866, 866, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 936, 1037, 1057, 
    3 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1194]

      4、 零散mysql查询记录:

     1 mysql> select count(*) from balancehistory order by shop_id,create_time;
     2 +----------+
     3 | count(*) |
     4 +----------+
     5 | 18723 |
     6 +----------+
     7 1 row in set (0.01 sec)
     8 
     9 mysql> select shop_balance,available_balance from shop where id = 4;
    10 +--------------+-------------------+
    11 | shop_balance | available_balance |
    12 +--------------+-------------------+
    13 | 129.42 | 37.1 |
    14 +--------------+-------------------+
    15 1 row in set (0.00 sec)
    16 
    17 mysql> select count(*) from balancehistory where shop_id = 4;
    18 +----------+
    19 | count(*) |
    20 +----------+
    21 | 1388 |
    22 +----------+
     1 mysql> select count(*) from balancehistory order by shop_id,create_time;
     2 +----------+
     3 | count(*) |
     4 +----------+
     5 | 18723 |
     6 +----------+
     7 1 row in set (0.01 sec)
     8 
     9 mysql> select shop_balance,available_balance from shop where id = 4;
    10 +--------------+-------------------+
    11 | shop_balance | available_balance |
    12 +--------------+-------------------+
    13 | 129.42 | 37.1 |
    14 +--------------+-------------------+
    15 1 row in set (0.00 sec)
    16 
    17 mysql> select count(*) from balancehistory where shop_id = 4;
    18 +----------+
    19 | count(*) |
    20 +----------+
    21 | 1388 |
    22 +----------+
     1 select shop_balance from customer_shop_follow where shop_id = 197 and customer_id = (select customer_id from senguocc.order where num = '197000154') ;
     2 select shop_balance from customer_shop_follow where shop_id = 197 and customer_id = (select customer_id from senguocc.order where num = '197000155') ;
     3 select shop_balance from customer_shop_follow where shop_id = 197 and customer_id = (select customer_id from senguocc.order where num = '197000156') ;
     4 select shop_balance from customer_shop_follow where shop_id = 661 and customer_id = (select customer_id from senguocc.order where num = '661000036') ;
     5 select shop_balance from customer_shop_follow where shop_id = 661 and customer_id = (select customer_id from senguocc.order where num = '661000037') ;
     6 select shop_balance from customer_shop_follow where shop_id = 848 and customer_id = (select customer_id from senguocc.order where num = '848000285') ;
     7 
     8 select count(*) from balancehistory order by shop_id,create_time;
     9 
    10 select id,create_time,shop_id,balance_type,balance_record,balance_value,shop_totalPrice,available_balance,is_cancel from balancehistory order by shop_id,create_time;

      5、 order表中的del_reason字段可以判断订单删除或取消的原因:若del_reason字段为'timeout'则表示是在线支付未付款(status = -1)超时造成的订单取消;若del_reason为空,则表示是用户取消的;若del_reason非空且不为'timeout',则表示是商家删除的.此外,因为现有订单表中没有记录订单的取消或删除时间,所以默认设定创建时间后的15分钟为删除时间. 

      6、 *筛选出shop_id错误的记录并修正.

        *将balancehistory表中的所有店铺id查出来存放在列表shop_list_query1中
        *将系统中的所有status > -1的订单的数量不为0店铺的id查询出来存在一个列表shop_list_query2中
        *然后从列表shop_list_query2中除去shop_list_query1中的id
        *设定一个change_shop_id列表,把balancehistory表中修改过的店铺的id都存进去.
        *遍历shop_id_list1每一个shop的id
        *查询店铺号在balancehistory中对应的shop_totalprice最新记录
        *查询店铺号在balancehistory中对应的available_balance最新记录
        *查询店铺号在shop表中对应的shop_totalprice和available_balance字段值
        *检查每一个店铺的店铺余额是否与balancehistory中的一致
        *判断shop表和balancehistory表两表中的shop_totalPrice和available_balance两个字段是否分别一致
        *查询balancehistory表中的所有余额支付(balance_type = 1)的记录的订单号并存到列表balance_type_1中
        *查询order表中的所有余额支付(pay_type = 2)的记录的订单号并存到列表pay_type_2中
        *判断pay_type_2的长度是否大于balance_type_1,若大于,则说明order表中肯定有余额支付的订单记录没有插入到balancehistory表中,这时就要把相关记录插入到balancehistory表中.
      7、 python获取过15分钟以后的时间:

    1 >>>import datetime
    2 >>> now = datetime.datetime(2015,12,31,23,59,50)
    3 >>> now
    4 datetime.datetime(2015, 12, 31, 23, 59, 50)
    5 >>> now = now + datetime.timedelta(minutes=15)
    6 >>> now
    7 datetime.datetime(2016, 1, 1, 0, 14, 50)

      8、 python列表去重的二种方法:
        第一种

    1 def delRepeat(liebiao):
    2     for x in liebiao:
    3         while liebiao.count(x)>1:
    4         del liebiao[liebiao.index(x)]
    5     return liebiao    

        第二种 无法保持原有顺序

    1 liebiao=set(liebiao)

      9、 sqlalchemy存储过程

      10、 现在已经把order表中漏的记录都插到balancehistory表中了,期间遇到的最大的问题就是5月10号之前因为没有开通在线支付,只有余额支付才能在balancehistory表中产生记录,而且当时可能也还没有available_balance字段,换句话说,在5月10号之前这个时间段中,只要用户往一个店铺中充了余额,那么不管这个余额用还是没有用,店铺管理员都是可以提现的。折腾了这么几天发现出错的地方也绝大部分都集中在这个时间段,大多都是余额支付或者余额支付的订单完成或者余额支付的订单删除的记录没有插入到表中,只有一少部分可能是因为线程竞争或者并发操作等其他原因导致的错误。牵一发而动全身,下一步就是修改balancehistory表中凡是出现了上述问题的所有shop_totalPrice和available_balance字段的值。  
        修改的大致思路如下:按照时间升序和按照店铺id分组遍历整张balancehistory表,当前记录的shop_totalPrice和available_balance字段的值可以根据当前的balance_value,balance_type以及上一条记录的shop_totalPrice和available_balance字段的值推算出来。
        鉴于有这么个特殊情况:当balance_type not in [2,6,7]时,available_balance都为0,所以当要推算当前记录的available_balance字段的值时,可能上一条记录中并没有available_balance的正确值(上一条的balance_type not in [2,6,7],故available_balance为无效的0),所以还要从当前位置开始往前遍历,直到遍历到第一条balance_type in [2,6,7]的记录,并读取其available_balance值,然后加上当前记录的balance_value值即可得当前记录的available_balance值。 
        实现方式上,一开始想的是利用mysql的存储过程遍历整张表,然后按照上面的方法修改,但是后来考虑了一下,存储过程还是有点复杂了,还不如先循环遍历表中所有的店铺id(或者只遍历进行了修改的店铺的id),然后在每一次循环中按照时间升序,把当前店铺对应的每条记录的:id,create_time,balance_type,balance_value ,shop_totalPrice,available_balance这些字段查询出来并且存放到一个二维的列表中。然后遍历这个列表,从第二项开始,第i项的shop_totalPrice和available_balance元素的值都可以用上面的算法计算出来然后存到原处。等遍历完这个列表以后,就可以根据id项通过sqlalchemy修改相应的字段了.
      11、 今日数据:快盘,github.


    [7.31 周五]

      1、 今日任务:余额错误对账
      2、 数据临时记录:

     1 //(改错前):
     2 mysql> select id,shop_name,shop_balance,available_balance from shop where shop_balance < available_balance;
     3 +------+-----------+--------------+-------------------+
     4 | id | shop_name | shop_balance | available_balance |
     5 +------+-----------+--------------+-------------------+
     6 | 1080 | 果缤纷 | 125 | 206.5 |
     7 +------+-----------+--------------+-------------------+
     8 
     9 mysql> select shop_balance,available_balance from shop where id = 4;
    10 +--------------+-------------------+
    11 | shop_balance | available_balance |
    12 +--------------+-------------------+
    13 | 155.42 | 58.1 |
    14 +--------------+-------------------+
    15 
    16 
    17 //(改错后):
    18 mysql> select shop_balance,available_balance from shop where id = 4;
    19 +--------------+-------------------+
    20 | shop_balance | available_balance |
    21 +--------------+-------------------+
    22 | 155.42 | 311.6 |
    23 +--------------+-------------------+
    24 
    25 mysql> select shop_balance from customer_shop_follow where shop_id = 1080 and customer_id = 2105;
    26 +--------------+
    27 | shop_balance |
    28 +--------------+
    29 | -75 |
    30 +--------------+
    31 1 row in set (0.00 sec)
    32 
    33 mysql> select sum(balance_value) from balancehistory where balance_type = 0 and customer_id = 2105 and shop_id = 1080;
    34 +--------------------+
    35 | sum(balance_value) |
    36 +--------------------+
    37 | 22 |
    38 +--------------------+
    39 1 row in set (0.00 sec)
    40 
    41 mysql> select sum(balance_value) from balancehistory where balance_type = 1 and customer_id = 2105 and shop_id = 1080;
    42 +--------------------+
    43 | sum(balance_value) |
    44 +--------------------+
    45 | 98.29999947547913 |
    46 +--------------------+
    47 1 row in set (0.00 sec)
    48 
    49 mysql> select sum(balance_value) from balancehistory where balance_type in (6,7) and customer_id = 2105 and shop_id = 1080;
    50 +--------------------+
    51 | sum(balance_value) |
    52 +--------------------+
    53 | 61.19999957084656 |
    54 +--------------------+
    55 1 row in set (0.00 sec)
    56 
    57 mysql> select sum(balance_value) from balancehistory where balance_type = 0 and customer_id = 2105 and shop_id = 1080;
    58 +--------------------+
    59 | sum(balance_value) |
    60 +--------------------+
    61 | 22 |
    62 +--------------------+
    63 1 row in set (0.00 sec)

      3、 修改思路可以简化:还是按之前的流程,只不过每一个店铺从头到尾balancehistory的每一条记录都赋上相应的值,为了简便计算.
        此外,在往balancehistory表中插入遗漏的记录之前,还要先找出错插的记录(比如一个店铺的记录的店铺id错误的记成了另外一个店铺的id),然后把店铺id改成正确的.

    1 select id,create_time,shop_id,balance_type,balance_record,balance_value,customer_id,customer_totalPrice,shop_totalPrice,available_balance,is_cancel from balancehistory where shop_id = 1080 
    2 order by create_time;
    3 select sum(balance_value) from balancehistory where balance_type = 1 and customer_id = 2105 and shop_id = 1080;
    4 select id,create_time,shop_id,balance_type,balance_record,balance_value,customer_id,customer_totalPrice,shop_totalPrice,available_balance,is_cancel from balancehistory order by 
    5 shop_id,create_time;
    6 select balance_record from balancehistory where balance_type = 3 and (balance_record not like '在线支付(微信):订单%' or balance_record not like '在线支付(支付宝):订单%');
    7 
    8 select id,create_time,shop_id,balance_type,balance_record,balance_value,customer_id,customer_totalPrice,shop_totalPrice,available_balance,is_cancel from balancehistory order by 
    9 shop_id,create_time;

      4、 从数据库中删除数据示例:

    1 self.session.query(models.CheckProfit).filter(models.CheckProfit.create_time == end_date,models.CheckProfit.is_checked==0).delete()

      5、 今日数据:快盘,github. 

    【8.3 周一】

      1、 今日任务:总后台销售统计功能
        *框架搭建:url,页面布局,echarts框架
        *后台数据获取:数据获取数据结果及算法

      2、 加功能流程:
      ①先确定在哪个模板网页(xxxbase.html)上加功能(方法:根据要添加新功能页面的url从urls.py文件中反查到对应的py文件中的类,然后到相应的py文件中找到这个类的get方法,到get方法的最后找到render方法,render方法中渲染的html页面即为当前的页面,然后找到这个html页面文件,即可找到它继承的base页面)--->②在该网页上加上需要的html元素--->③在该网页的顶部设置该元素的激活状态变量,并设定激活条件,以及该元素的跳转href名称,这样写:href={{reverse_url('superCheckCash')}}--->④在urls.py中添加该名称对应的路由以及superadmin.py中的新增加类的名称--->⑤在superadmin.py中增加新的类,实现后台功能.先从简单开始,只写get方法,return渲染到新页面,之后再丰富功能--->⑥添加名字为第⑤步中的渲染的网页名称的新网页,引用需要的基本框架,在其中添加简单的字符,以及在下方添加引用其对应的js文件--->⑦新建第⑥步中需要的js空文件--->⑧运行系统,看新增加的链接和页面能否正常加载.如果正常加载,则进一步丰富系统功能(写后台post方法,html页面的元素,js的事件处理等).

      3、 系统预定义的水果的名称,id及其他信息存在db_initdata.py文件中.

      4、 功能简述:
        1.商品按类目销售统计(按照日/周/月,分别统计不同时间段的系统中总销售额前30名的商品类目及销售额)
        2.同种类目商品销售额前10名店铺统计(按照日/周/月,分别统计不同时间段同种类目商品(限于1中的30个类目)销售额达到系统前十名的店铺的详细信息)
        3.相同分组商品店铺销售额Top30店铺
        4.不同分组商品销售额排行(就三组:水果,干果,其他商品)(最后再加上个饼形图)

      5、 注意事项:
        1.因为每个店铺的每单种商品的id具有唯一性,同一种商品(比如阿克苏香梨)不同店铺的叫法也可能不同,而且不同店铺同一类目下的商品都不相同,所以总后台的销售统计中类目销售统计不能统计类目下具体商品的销售情况.
        2.统计销售额最大的前若干种商品具有现实意义,可以通过分析不同时间(或季节)不同热卖的商品的变化趋势,得出一些有价值的结论,为商城商家提供信息.
        3.因为图表比较长,所以要加查看大图功能.
        4.图表名称:
          商品销售额Top30 - 按商品类目排序
          同类商品店铺销售额Top10 - 按店铺名排序
          同组商品店铺销售额Top10 - 按店铺名排序

      6、 微信公众号文章排版工具:秀米

      7、 想要给总后台的订单统计的echarts添加loading,但改了半天发现出各种错误,还是当初在似懂非懂的时候写出的代码太混乱了,结构不清晰,变量不规范,有必要进行重构.

      8、 今天总后台订单统计出现一个bug:下单时间曲线和收货时间曲线又完全一样了,提示错误:KeyError:26
        马上意识到又是数组下标越界了,把superadmin.py的954行左右的代码改成如下即可:
    if order[1].hour + (order[1].minute+order[3])//60 >= 24:
    data[order[1].hour + (order[1].minute+order[3])//60 - 24] += 1

    【8.4 周二】
      1、 今日任务:总后台销售统计功能
      2、 后台数据获取算法:
        *销售额Top30商品类目:
        (1)从fruit_type表中查出所有商品类目的id和名称,存到一个字典fruit_type_price_dict中,键为id,值为名称和销售额组成的列表,且销售额都初始化为0.
          select id,name from fruit_type;
        (2)获取指定时间段内的所有订单的fruits字段和mgoods字段并分别存到两个列表中:fruit_list,mgoods_list
        (3)从fruit_list列表的每一项中分离出charge_type_id和对应的金额,然后从charge_type_id反查到fruit_type_id,最后将fruit_type_price_dict中与此id相同的项的金额自加这个金额.
          计价方式-商品类目id对照表:select distinct charge_type.id,fruit_type_id from fruit,charge_type where fruit.id = charge_type.fruit_id;
        (4)从mgoods_list列表的每一项中分离出对应的金额,直接将fruit_type_price_dict中id=2000项(其他商品)的金额自加这个金额.
        (5)将字典fruit_type_price_dict转化成列表fruit_type_price_list,该列表的每一个元素都为一个子列表,子列表有三个元素:商品类目id,名称,金额.将fruit_type_price_list列表按照每一项的金额降序排序,取前30个元素作为output_data,返回给前台.
        *同类商品销售额Top10店铺:
        (1)在网页刷新的时候发一个post请求,用于获取所有商品类目的id和名称,按id升序排列,然后传到js中为下拉选择二级菜单赋值,将类目名称赋给下拉按钮的text,将id赋给下拉按钮的data-id属性.令cur_selected_type_id = 当前显示的选中那个类目的id.
        (2)通过post请求将cur_selected_type_id连带其他参数(起止时间等)传到后台,后台shop_count方法进行处理.
        (3)先从fruit表和charge_type表联合查询查出所有fruit_type_id等于cur_selected_type_id的charge_type.id,shop_id,并建立一个字典charge_type_shop_dict,该字典的键是charge_type.id,值是一个列表,列表的第一项是shop_id,第二项是金额,初始化为0.
    select charge_type.id,shop_id from charge_type,fruit where charge_type.fruit_id = fruit.id;
        (4)用self.get_order方法获取选取时间段的所有订单的fruits字段和mgoods字段并存入fruit_list和mgoods_list中,并判断如果cur_selected_type_id != 2000则不对mgoods进行处理,只对fruit_list进行处理.
        (5)从fruit_list列表的每一项中分离出charge_type_id和对应的金额,然后将charge_type_shop_dict键等于charge_type_id的项的金额自加对应的金额.如果查不到计价方式对应的shop_id(可能计价方式已经被删除了)那么直接continue.需要建立一个列表cur_charge_type_list,该列表包含cur_selected_type_id对应的所有计价方式的id.
        (6)将字典charge_type_shop_dict的值([shop_id,price])存到一个列表中,考虑到shop_id会有重复的情况(因为同一个charge_type_id可能对应多个shop_id),所以先要把shop_id存到一个列表cur_shop_id中,然后将该列表去重,然后再遍历charge_type_shop_dict的值,累加每个店铺的price,存到each_shop_price_dict字典中,键为shop_id,值为price.
        (7)如果cur_selected_type_id == 2000:从m_charge_type,m_goods,menu三表联合查询,查出所有m_charge_type中id对应的menu表中的shop_id,并建立字典,键为m_charge_type_id,值为shop_id,然后根据shop_id将金额累加到charge_type_shop_dict中去.如果查不到计价方式对应的shop_id(可能计价方式已经被删除了)那么直接continue.
    完了以后再遍历当前字典,如果shop_id在each_shop_price_dict的键列表中,则将这这个键对应的price自加,否则新增键值对.
        (8)最后新建一个列表each_shop_price_list,每一个元素为一个子列表,子列表的第一项为shop_id,第二项为shop_name,第三项为price,将each_shop_price_list按照price降序排列,然后取each_shop_price_list的前十项返回到前台.
        *同组商品销售额Top10店铺:
        (1)先设定三个分组的id:水果1,干果2,其他商品0,页面刷新的时候前台就发post请求,传分组id,然后后台group_count方法进行处理
        (2)后面处理算法与前面类似.

      3、 注意事项:
        (1)fruit_type_id < 999:水果
        (2)fruit_type_id = 999:其他水果
        (3)1000 < fruit_type_id < 1999:干果
        (4)fruit_type_id = 1999:其他干果
        (5)fruit_type_id = 2000:其他商品
        (6)之前的mgoods现在都算成是'其他商品'
        (7)有大量的fruits字段中的计价方式被删掉了,这样就找不到该种水果所属的类别了,那金额就统一归为'其他水果'的类目中.
        (8)第二个图的下拉选项按钮考虑用二级菜单实现,列出所有的商品类目供选择.

      4、 python在当前类中调用当前类的其他方法,在其他方法名前一定要加'self.'才行,否则会提示方法未定义.

      5、 jquery两个关联元素绑定hover事件(这个问题好麻烦,网上找了很多资料都很麻烦,最后发现了setTimeout函数)
        解决方案1:用延时函数解决.

    1 setTimeout(function(){
    2 ... //这里是要延时500ms执行的代码
    3 },500);

        因为给第二级菜单加了mouseover事件来显示第三级菜单,同时也给第二级菜单加了mouseleave事件在鼠标离开第二级菜单的时候隐藏第三级菜单,但是在鼠标从第二级菜单向第三级菜单移动的时候,本来是不期望隐藏掉第三级菜单的,因为那样就找不到第三级菜单了,所以为第二级菜单的mouseleave事件加上延时,这样鼠标指针就可以在第三级菜单隐藏掉之前滑到它上面,同时在第二级菜单的延时时间到达的那一瞬间用show()方法显示.这样就解决了问题,但是会出现闪烁的现象.

        解决方案2:用联动的addClass("hidden")和removeClass("hidden")方法实现,此种方法可以方案一的闪烁现象,但也有缺陷.
             假设第二级菜单有两个选项,id分别为second1,second2,点击second1,second2弹出的第三级菜单的id分别为third1,third2,先给second1添加mouseover事件:

    1 $("#third1").removeClass("hidden");
    2 $("#third2").addClass("hidden");
    3 //再给third1添加mouseover事件:
    4 $("third1").removeClass("hidden")
    5 //给third1添加mouseout事件:
    6 $("third1).addClass("hidden")

             对second2和third2的处理同理,只要保证third1和third2的隐藏和消失互斥即可.

    【8.5 周三】
      1、 今日任务:总后台销售统计

      2、 jquery有时候要显示一个元素,仅仅使用removeClass("hidden")属性是不管用的,display属性仍然为none,还要再加个show()方法.

      3、 jquery display:none与visible:hidden的区别:
        display:none和visible:hidden都能把网页上某个元素隐藏起来,但两者有区别:
        display:none --- 不为被隐藏的对象保留其物理空间,即该对象在页面上彻底消失,通俗来说就是看不见也摸不到。
        visible:hidden--- 使对象在网页上不可见,但该对象在网页上所占的空间没有改变,通俗来说就是看不见但摸得到。
        例子:

    1 <html>
    2 <head>
    3 <title>display:none和visible:hidden的区别</title>
    4 </head>
    5 <body >
    6 <span style="display:none; background-color:Blue">隐藏区域,同时把位置让出来</span><span style=" background-color:Green">显示区域,显示的地方会占据原先隐藏区域的位置</span><br />
    7 <span style="visibility:hidden; background-color:Blue">隐藏区域,虽然隐藏但是位置依然占着</span><span style="background-color:Green">显示区域</span>
    8 </body>
    9 </html>

      4、 jquery中调用post方法所在的函数可能会出现这个问题:
        前一个调用还没有执行完(比如因为数据量大等因素),后一个调用紧接着就执行了,这会带来相互之间的影响,引发未知的错误.
        可以这样解决:定义一个全局的标志变量,类似于一个锁,用该变量的状态控制每次的调用是否能够执行即可.或者关闭异步:$.ajaxSetup({async:false});

      5、 用echarts的hideLoading方法时遇到一个奇怪的问题:刷新页面的时候js出现异常,出现错误:

    1 Uncaught TypeError: Cannot read property 'hideLoading' of null(anonymous function) @ superadmin-count_sell.js?v=03a80c5e1ec3f44ec6876f69099b1146:862m.Callbacks.j @ jquery.min.js:2m.Callbacks.k.fireWith @ jquery.min.js:2x @ jquery.min.js:4m.ajaxTransport.send.b @ jquery.min.js:4
    2 echarts.js:1 Dom’s width & height should be ready before init.

        而且这个问题有时出现有时又正常.
        但当点击选项的时候调用同样的post请求函数,又恢复正常.
        最后发现问题出在:初始化echarts的时候没有为echarts所在的div设定高度,导致echarts初始化失败,只需提前为div设置高度即可.
        但偶尔数据返回成功以后,还会出现异常:Uncaught TypeError: Cannot read property 'hideLoading' of null,也就是echarts还是没有初始化成功.这个异常只是偶尔出现,别的时候恢复正常,可是这更加让人困惑.
        找了半天原因发现有这么一个现象:在浏览器中用ctrl+R或者f5刷新才会偶尔出现上述问题,而从get方法进入或者从url回车进入并不会出现这个问题,所以猜测可能是用ctrl+R或f5刷新的时候导致浏览器强制清空了变量的缓存,而这时函数可能还没有执行完,接着再执行到了用到刚刚清空了缓存的变量的地方,就会出现变量诡异的变成了null的现象.
        而且还有就是做店铺后台销售统计的时候也并没有提前设置高度,而且没有出现异常,这个问题有待进一步探究.
        后来研究echarts官网的demo发现,返回数据后需要对hideLoading方法进行一个延时处理,所以考虑可能是因为echarts异步刷新的问题,但是延时处理以后还会偶尔出现异常,也可能是echarts的一个bug.

      6、 在浏览器中打开一个纯文本txt编辑器页面:
        在地址栏中输入:data:text/html, <textarea style='height:100%;100%;margin:auto'></textarea>然后回车即可;
        如果想保留文本的格式和图片,则只需输入这行代码即可:data:text/html, <html contenteditable>
        还可以把这两个页面保存为书签,或在输入内容以后保存成文件,以后点开就能用.

      7、 总后台添加了省级代理管理员,增加管理员的level(总超级管理员:0,省级代理管理员:1,已删除:-1)

    1 self.current_user.level
    2 shop_province = self.current_user.province

    【8.6 周四】
      1、 今日任务:
        (1)已经完成了总后台销售统计功能,但是考虑到系统新增加了总后台省级代理管理员,所以要根据管理员的level和province对数据进行筛选.
        (2)为店铺后台-商品管理-商品分组中的分组添加链接,跳转到'所有商品'并显示相应分组的商品.
        (3)店铺后台-商品关联-所有商品的排序有bug,修复之.
      2、 根据总后台管理员的level和province对数据进行筛选的基本方式举例:

    1 level = self.current_user.level
    2 shop_provice = self.current_user.province
    3 if level == 0:
    4   order_list_data = self.session.query(models.Order).filter(models.Order.status == 6).order_by(desc(models.Order.comment_create_date))
    5 elif level == 1:
    6   order_list_data = self.session.query(models.Order).join(models.Shop,models.Order.shop_id == models.Shop.id).filter(models.Order.status == 6,models.Shop.shop_province == shop_province).distinct(models.Order.id).order_by(desc(models.Order.comment_create_date))

        测试用数据:level = 1,shop_province = 420000

      3、 完成数据分省筛选!

      4、 echarts中series添加属性barWidth:[number]可以设置柱状条的宽度.

      5、 sqlalchemy三表联合查询示例:

    1 query_list = self.session.query(models.TableA.a_name).join(models.TableB,models.TableA.b_id == models.TableB.id).join(models.TableC,models.TableB.c_id = models.TableC.id).filter(models.TableC.name == 'ccc').all()

      6、 html中如何让鼠标箭头移到某个div上就变为手型,移出后再变为原型:给这个div的样式加一条:'cursor:pointer;'如果被覆盖了,就在pointer后面加一个'!important'

      7、 jquery中网址跳转示例('?'后面的是参数):window.location.href = "/admin?data=data_str&&page=0";

      8、 jquery,点击DIV触发事件,但是点击DIV的子元素不触发事件,该如何写选择器?
        解决方法:即取消事件冒泡:

    1 $('.div a').click(function(e){
    2 e.stopPropagation();
    3    ...//a标签的其他事件处理
    4 });

        然后为div添加点击事件即可.
        主要就是让a标签点击了以后停止冒泡,'.div'就是a标签外层容器的class.

      9、 chrome事件调试:
        在要检查的元素上单击右键选择查看元素,然后,右边的面板中会显示style标签,切换到EventListenrs标签,可以看到相关的事件绑定信息。点击最右边的文件名称还可以跳转到事件定义代码在脚本文件中的位置。
        或者可以使用一个更强大的插件:chrome VisualEvent,直接搜索VisualEvent.crx下载后直接拖放到浏览器中安装即可.

      10、 util.js里面有很多好方法.
          base.py里面也有很多好方法.

    【8.7 周五】
      今日任务:店铺后台-商品关联-所有商品的排序有bug,修复之.
      1、 关于reverse_url和static_url两个函数:
        (1)reverse_url:
          ①在goods-set-base.html中,有几个链接,其中用到reverse_url函数:

    1 <ul class="subnav pull-left order-type">
    2 <li class="text-center aall"><a href="{{reverse_url('adminGoods')}}">所有商品</a></li>
    3 <li class="text-center agroup"><a href="{{reverse_url('adminGoodsGroup')}}">商品分组</a></li>
    4 <li class="text-center adelete"><a href="{{reverse_url('adminGoodsDelete')}}">已删除商品</a></li>
    5 <li class="text-center ml40 aclassify"><a href="{{reverse_url('adminGoodsClassify')}}">商品类目</a></li>
    6 </ul>

          ②在urls.py中有如下几个url:

    1 ...
    2 (r"/admin/goods/all", handlers.admin.Goods, {"action":"all"}, "adminGoods"),
    3 (r"/admin/goods/classify", handlers.admin.Goods, {"action":"classify"}, "adminGoodsClassify"),
    4 (r"/admin/goods/group", handlers.admin.Goods, {"action":"group"}, "adminGoodsGroup"),
    5 (r"/admin/goods/delete", handlers.admin.Goods, {"action":"delete"}, "adminGoodsDelete"),
    6 ...

          ③在admin.py的Goods类中,有初始化方法:

    1 @tornado.web.authenticated
    2 def initialize(self,action);
    3   self._action = action

          ④在Goods类的get方法中调用action:

    1 @AdminBaseHandler.check_arguments(...)
    2 def get(self):
    3     action = self._action
    4     ...

          ⑤reverse_url是tornado自带的函数,用来根据name来反查url.
        (2)static_url,也是tornado自带的函数:
          ①在application.py文件的setting中有:static_path=os.path.join(os.path.dirname(__file__),"static")
          ②在template/index.html中有:

    1 <head>
    2 <link rel="stylesheet" type="text/css" href="{{ static_url('css/style.css')}}">
    3 <head>

      2、 python销毁一个变量x:del x

      3、 js中ajax的使用:

     1 function getData(action,id,page){
     2 var url;
     3 url = "/admin/goods/all?action=" + action + "&id=" + id + "&page=" + page;
     4 $.ajax({
     5 url:url,
     6 type:"get",
     7 success:function(res){
     8 if(res.success){
     9 output_data = res.output_data;
    10 console.log(output_data);
    11 }
    12 else{
    13 Tip(res.error_text);
    14 }
    15 }
    16 })    
    17 }

        然后在后台的相应类的get方法中要这样写:

    1 ...
    2 @AdminBaseHandler.check_arguments("action:str","id:int","page:int")
    3 def get(self):
    4     action = self.args["action"]
    5     id = self.args["id"]
    6     page = self.args["page"]
    7     output_data = [action,id,page]
    8     return self.render("admin/goods-all.html",output_data=output_data)
    9     ...

      4、 js实现这样一个功能:点击一个页面A中的a标签跳到另外一个页面B,同时向B页面的后台发送请求返回相应的数据刷新页面:
        (1)在A页面的html文件中为要添加跳转链接的a标签的href属性赋值(包括js中动态添加的a标签也要赋值),其中前半部分为要跳转到的B页面的url,后半部分为自定义的要传递的参数.这里的data["user_id"]需要提前向A页面的后台请求获取到.

    1 <a href="/super/user?out_link=true&&data_id={{data["user_id"]}}" ...>...</a>

        或者用reverse_url函数:

    1 <a href="{{reverse_url('superUser')}}?out_link=true&&data_id={{data["user_id"]}}" ...>...</a>

        (2)在B页面的js中的$(document).ready(function(){})中添加类似于如下的代码:

    1 var out_link = $.getUrlParam("out_link");
    2 if(out_link == "true"){
    3 var data_id = $.getUrlParam("data_id");
    4 page = 0;
    5 getSearchContent('out_link',data_id,page); //这里getSearchContent为B页面的js中原来就有的搜索函数,这里借用一下,只是把action
    6 //从'search'改成了'out_link',把传给后台的参数从inputinfo改成了data_id
    7 //当然也可以自己重新再写一个函数向后台请求数据.
    8 }

        (3)在B页面的后台中,增加如下代码:

    1 ...
    2 action = self.args["action"]
    3 ...
    4 if action == "out_link":
    5 user_id = int(self.args["inputinfo"])  //这里的参数inputinfo正是(2)中js传过来的参数data_id.
    6 q = q.filter(models.Accountinfo.id == user_id)
    7 ... #后面的是对q进行处理从而返回给js数据的代码

        注:跳转后要把跳转到的页面的"上一页/下一页"按钮隐藏掉

      5、 python eval函数的强大:
        可以将字符串str当成有效的表达式来求值并返回计算结果。
        例如:

    1 if sort_way = "group":
    2     case = 'models.Fruit.group_id'
    3 elif sort_way = "type":
    4     case = 'models.Fruit.fruit_type_id'
    5     goods = self.session.query(models.Fruit.name).order_by(eval(case_one)).all()

      6、 mysql中按照拼音排序的方法:
        select name from user order by convert(name using gbk) asc;
        但这种方法在sqlalchemy中用不了,无论是这样用:
          name_list = self.session.query(models.User.name).order_by(func.convert(models.User.name.using('gbk'))
        还是这样用:
          name_list = self.session.query(models.User.name).order_by(func.convert(models.User.name,'gbk'))
        或者这样用:
          name_list = self.session.query(models.User.name).order_by(func.convert(models.User.name,'gbk','utf-8'))
        或者这样用:
          name_list = self.session.query(models.User.name).order_by(func.encode(models.User.name,'gbk'))
        结果都会报错,看来sqlalchemy是不支持这种编码转换的.

    【8.8 周六】

      1、 python 3中只有unicode str,把decode方法去掉了
      2、 python多维排序示例(用operator):

    1 >>> from operator import itemgetter
    2 >>> c = [[1,2,5],[2,43,23],[1,1,32],[3,34,6],[3,2,5],[3,2,4]]
    3 >>> c.sort(key=operator.itemgetter(0,1,2))
    4 >>> c
    5 [[1, 1, 32], [1, 2, 5], [2, 43, 23], [3, 2, 4], [3, 2, 5], [3, 34, 6]]
    6 >>> b = [{'b': 211, 'a': 100}, {'b': 21, 'a': 100}, {'b': 323, 'a': 101}, {'b': 12, 'a': 100}]
    7 >>> b.sort(key = itemgetter('a','b'))
    8 >>> b
    9 [{'b': 12, 'a': 100}, {'b': 21, 'a': 100}, {'b': 211, 'a': 100}, {'b': 323, 'a': 101}]

      3、 因为gbk/gb2312的坑爹编码方式,前几千个一级汉子是连续的且按拼音排序的,但后面的汉子却不是按照拼音排序的,所以中文拼音排序仍然还是一个问题,有待解决.

      4、 gb2312是gbk的子集,两者都是16位的,gbk是gb18030的子集,gb18030是32位的.

      5、 总后台用户名链接的精确跳转的a标签href属性设置:

    1 href="/super/user?out_link=true&&data_id={{shop.admin.accountinfo.id}}" target="_blank" title="点击查看用户详细信息"
    2 href="{{reverse_url('superUser')}}?out_link=true&&data_id={{shop.admin.accountinfo.id}}" target="_blank" title="点击查看用户详细信息"

    【8.10 周一】

      1、 sqlalchemy执行原生sql语句的方法:

    1 entries = self.session.execute("select name from fruit where shop_id = 4")
    2 print(entries)
    3 for item in entries:
    4    print(item)

        输出结果为:
        <sqlalchemy.engine.result.ResultProxy object at 0x7f771a669860>
      2、 近期的店铺后台加了防DDoS攻击的代码,因此导致如果后台操作过于频繁会出现503错误,之前写的销售统计也会出现这个问题.
        解决方法:在js中每次发post请求之前都进行延时(这里延时了250ms)
      3、 503错误:503 (Service Temporarily Unavailable):
        日志文件中,其中的s-reason项:
        (1)若为AppShutdown,可能是由于CPU占用率太高导致自动关闭应用程序池。
        (2)若为AppOffline,可能是由于应用程序标识出错引起的。
        (3)若为Disabled,可能是由管理员手工关闭应用程序池引起的。
        (4)若为QueueFull,可能是因为请求时应用程序池队列已满而生成该错误。
      4、 DDos攻击的比喻说明:
      一群恶霸试图让对面那家有着竞争关系的商铺无法正常营业,他们会采取什么手段呢?(只为举例,切勿模仿)恶霸们扮作普通客户一直拥挤在对手的商铺,赖着不走,真正的购物者却无法进入;或者总是和营业员有一搭没一搭的东扯西扯,让工作人员不能正常服务客户;也可以为商铺的经营者提供虚假信息,商铺的上上下下忙成一团之后却发现都是一场空,最终跑了真正的大客户,损失惨重。此外恶霸们完成这些坏事有时凭单干难以完成,需要叫上很多人一起。嗯,网络安全领域中DoS和DDoS攻击就遵循着这些思路。
      在信息安全的三要素——“保密性”、“完整性”和“可用性”中,DoS(Denial of Service),即拒绝服务攻击,针对的目标正是“可用性”。该攻击方式利用目标系统网络服务功能缺陷或者直接消耗其系统资源,使得该目标系统无法提供正常的服务。
      DdoS的攻击方式有很多种,最基本的DoS攻击就是利用合理的服务请求来占用过多的服务资源,从而使合法用户无法得到服务的响应。单一的DoS攻击一般是采用一对一方式的,当攻击目标CPU速度低、内存小或者网络带宽小等等各项指标不高的性能,它的效果是明显的。随着计算机与网络技术的发展,计算机的处理能力迅速增长,内存大大增加,同时也出现了千兆级别的网络,这使得DoS攻击的困难程度加大了-目标对恶意攻击包的"消化能力"加强了不少。这时候分布式的拒绝服务攻击手段(DDoS)就应运而生了。DDoS就是利用更多的傀儡机(肉鸡)来发起进攻,以比从前更大的规模来进攻受害者.
      5、 总后台余额和对账有bug,提示404错误和一个返回数据undefined错误.
        错误原因:一个公用的html文件引入了子html文件的js,导致查询不到数据,把这个多余的js去掉即可.
      6、 如何提高python服务器的并发度?瓶颈在哪?如何打破瓶颈?
        node.js和python的区别.
        操作系统/linux的知识.
        缓存持久化
      7、 压缩的js或html文件还原工具:http://jsbeautifier.org/
      8、 新功能:后台-用户管理的排序规则完善:建议添加用户积分、充值金额的排序规则.
      9、 用get方法代替post,比较简单!方法总结.
      10、 联合查询后的分页问题(分页错乱).
    【8.11 周二】

      今日任务:de各种bug,增加店铺后台用户管理的排序功能.
      新功能:商品秒杀的店铺后台前端与后端实现.
      1、 昨天的任务完成!分页错乱的问题是因为联合查询的时候没有筛选店铺id导致的.
      2、 下拉选择按钮的实现总结:
      (1)首先在html的适当位置加入如下代码:

     1 <li class="pull-right set-right10"> 
     2         <div class="tit cate-title">排序规则:</div>
     3         <div class="dropdown slide-down-select w130">
     4                 <button class="btn btn-default dropdown-toggle area height25 slide-btn" type="button" id="dropdownMenu1" data-toggle="dropdown">
     5                     <em class="pull-left filter_status" id="cur-sort-way" data-id="time">关注时间</em>
     6                     <span class="caret pull-right no-margin-left rotate0"></span>
     7                 </button>
     8                 <ul class="dropdown-menu dropdown-menu-right w130 condition-list" role="menu" aria-labelledby="dropdownMenu1" id="sort-way-list">
     9                         <li data-id="time"><a class="item sort-time">关注时间</a></li>
    10                         <li data-id="point"><a class="item sort-point">用户积分</a></li>
    11                         <li data-id="balance"><a class="item sort-balance">账户余额</a></li>
    12                 </ul>
    13         </div>
    14 </li>

        分析:①最外层的li有两个css属性,分别是向右浮动和右边距为10.css样式如下:
            .pull-right{float:right !important;}
            .set-right10{margin-right:10px;}
          ②第一个div有两个css属性,分别如下:
            .tit{display:inline-block;overflow:hidden;}
            .cate-title{font-size: 14px;color: #999;height: 28px;line-height: 28px;float: left;margin-right: 5px;}
          ③第三个div,有三个css属性,第一个属性用于下面的判断,后面两个属性分别如下:
            .slide-down-select{display:inline-block;100%;background:#fff;color:#000;border:1px solid #ddd;margin-left:0;}
            .w130{130px !important;}
          ④button中有很多属性,btn属性如下:
            .btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none; -ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius: 6px;}
          ⑤btn-default属性如下(应该是bootstrap自带属性):
            .btn-default{color:#333;background-color:#fff;border-color:#ccc}
            data-toggle为数据切换属性.
          ⑥其他属性如下:
            .dropdown-toggle{padding:0 4px;}
            .area{display:inline-block;100%;background:#fff;color:#222;border:1px solid #e7e7eb;margin-left:0;}
            .height25{height:25px;}
            .slide-down-select .slide-btn{height: 24px;line-height: 24px;display: block;border: none;outline: none; 100%;}
          ⑦所有属性整理如下:

     1 .dropdown-menu{overflow: hidden;max-height: 210px;overflow-y: auto;}
     2 .category-list>li{float: left;width: 25%;}
     3 
     4 .cate-title{font-size: 14px;color: #999;height: 28px;line-height: 28px;float: left;margin-right: 5px;}
     5 .slide-down-select{display:inline-block;width:100%;background:#fff;color:#000;border:1px solid #ddd;margin-left:0;}
     6 .slide-down-select .slide-btn{height: 24px;line-height: 24px;display: block;border: none;outline: none;width: 100%;}
     7 .slide-down-select em{font-style:normal;display:inline-block;width:110px;font-size:14px;overflow:hidden;padding-left: 10px;text-align: left;}
     8 
     9 .dropdown-toggle{padding:0 4px;}
    10 .dropdown-menu li{width:100%;padding:0;}
    11 .dropdown-menu .item{padding-left:14px;}
    12 .dropdown-menu li a:hover{color:#fff;background-color: #6ba4ef;}
    13 .caret{margin-top:10px;color: #c6c6c6;}
    14 .borderc{border: 1px solid #e7e7eb;}
    15 .pointer{cursor: pointer;}
    16 .min80{min-width: 80px;}
    17 .mt2{margin-top: 2px;}
    18 .txt-center{text-align: center !important;}
    19 .txt-left{text-align: left !important;}
    20 .hig{border: 1px dashed #ff6666 !important;}
    21 .no-title{font-size: 20px;text-align: center;margin-top: 40px;color: #333;}
    22 
    23 .pull-left{float:left !important;}
    24 .w130{width:130px !important;}
    25 .ml20{margin-left: 20px;}

        (2)然后在js中要为ul中的li添加点击事件,点击事件要做两件事,一是点击的时候为em的text赋值为当前li的a标签的text,二是要调用相关函数向后台发post请求显示数据.
        (3)ul在一开始的时候也可以是空的,在js中先向后台发post请求获取ul的项的数据,然后在js中动态添加上去,再添加点击事件.
      3、 用get方法替代post方法请求数据的方法(这种方法特别适合于页面结构特别复杂,写artTemplate很困难的情况的数据请求,也适合于翻页的数据请求):
        (1)先在后台写get方法,设置所需的参数检查,比如:
          @AdminBaseHandler.check_arguments("order_by:str","page:int")
          其中order_by表示在url中的排序方式参数,page表示页码.
        (2)在get方法中根据order_by和page向数据库查询数据,并做排序和分页处理,然后用数据渲染页面.
        (3)在js中,将order_by和page设置为全局变量,利用下拉选项按钮的点击事件改变order_by的值,利用上一页和下一页按钮的点击事件改变page的值,然后在这些点击事件中用这样的方法向后台请求数据和刷新页面:
          var url = "/XXX?order_by="+order_by+"&&page="+page;
          window.location.href = url;
        当然这只是一个简单的例子,还可以添加更为复杂的参数.
        注:在后台参数检查的时候判断某一个参数是否不在参数列表中的方法:
        if 'page' in self.args:
           ...
      4、 数据库联合查询:

     1 (1)mysql中,建立了两个表A和B:
     2 create table A (aID int auto_increment primary key,aNum char(10));
     3 create table B(bID int not null auto_increment primary key,bName char(10));
     4 (2)向A和B表中插入数据,结果如下:
     5 mysql> select * from A;
     6 +-----+------+
     7 | aID | aNum |
     8 +-----+------+
     9 |   1 | a101 |
    10 |   2 | a102 |
    11 |   3 | a103 |
    12 |   4 | a104 |
    13 |   5 | a105 |
    14 +-----+------+
    15 
    16 mysql> select * from B;
    17 +-----+-------+
    18 | bID | bName |
    19 +-----+-------+
    20 |   1 | b201  |
    21 |   2 | b202  |
    22 |   3 | b203  |
    23 |   4 | b204  |
    24 |   8 | b209  |
    25 +-----+-------+
    26 
    27 (3)测试左联接(left join):
    28 mysql> select * from A left join B on A.aID = B.bID;
    29 +-----+------+------+-------+
    30 | aID | aNum | bID  | bName |
    31 +-----+------+------+-------+
    32 |   1 | a101 |    1 | b201  |
    33 |   2 | a102 |    2 | b202  |
    34 |   3 | a103 |    3 | b203  |
    35 |   4 | a104 |    4 | b204  |
    36 |   5 | a105 | NULL | NULL  |
    37 +-----+------+------+-------+
    38 5 rows in set (0.00 sec)
    39 结果分析:left join是以A表的记录为基础的,A表可以看成是左表,B表可以看成是右表,left join是以左表为基准的.换句话说,作为基准的左表(A)的记录将会全部表示出来,而右表(B)
    40 
    41 只会显示符合搜索条件的记录(上例中为:B.bID=A.aID),B表没有记录的地方都为NULL.
    42 
    43 (4)测试右联接(right join):
    44 mysql> select * from A right join B on A.aID = B.bID;
    45 +------+------+-----+-------+
    46 | aID  | aNum | bID | bName |
    47 +------+------+-----+-------+
    48 |    1 | a101 |   1 | b201  |
    49 |    2 | a102 |   2 | b202  |
    50 |    3 | a103 |   3 | b203  |
    51 |    4 | a104 |   4 | b204  |
    52 | NULL | NULL |   8 | b209  |
    53 +------+------+-----+-------+
    54 5 rows in set (0.00 sec)
    55 结果分析:刚好和left join的结果相反,这次是以右表(B)为基准的,A表不足的地方用NULL填充.
    56 
    57 
    58 (5)测试inner join(相等联接或内联接)
    59 mysql> select * from A inner join B on A.aID = B.bID;
    60 +-----+------+-----+-------+
    61 | aID | aNum | bID | bName |
    62 +-----+------+-----+-------+
    63 |   1 | a101 |   1 | b201  |
    64 |   2 | a102 |   2 | b202  |
    65 |   3 | a103 |   3 | b203  |
    66 |   4 | a104 |   4 | b204  |
    67 +-----+------+-----+-------+
    68 4 rows in set (0.00 sec)
    69 结果分析:这次只列出了A.aID和B.bID相等且不为NULL的记录,并不以哪个表为基准.
    70 
    71 注:数据库中表名是分大小写的.
    72 sqlalchemy中只有join和outerjoin(为左外联接).

      5、 QQ分享API

     1 <script type="text/javascript">
     2 (function(){
     3 var p = {
     4 url:location.href, /*获取URL,可加上来自分享到QQ标识,方便统计*/
     5 desc:'', /*分享理由(风格应模拟用户对话),支持多分享语随机展现(使用|分隔)*/
     6 title:'', /*分享标题(可选)*/
     7 summary:'', /*分享摘要(可选)*/
     8 pics:'', /*分享图片(可选)*/
     9 flash: '', /*视频地址(可选)*/
    10 site:'', /*分享来源(可选) 如:QQ分享*/
    11 style:'101',
    12 96,
    13 height:24
    14 };
    15 var s = [];
    16 for(var i in p){
    17 s.push(i + '=' + encodeURIComponent(p[i]||''));
    18 }
    19 document.write(['<a class="qcShareQQDiv" href="http://connect.qq.com/widget/shareqq/index.html?',s.join('&'),'" target="_blank">分享到QQ</a>'].join(''));
    20 })();
    21 </script>
    22 <script src="http://connect.qq.com/widget/loader/loader.js" widget="shareqq" charset="utf-8"></script>

      6、 商品秒杀(seckill)任务分析:
        (1)原型图中'点击查看使用教程'的跳转链接为:'/bbs/detail/xxx',其中'xxx'为bbs中教程的编号,是一个整数.
        (2)原型-秒杀管理中,列表中'秒杀商品后面的数字去掉;'领取/下单/库存'列的数值为秒杀商品的总数.
          ('领取'是个什么意思??)
        (3)当过了秒杀的时间,该行秒杀管理文字颜色全部变为灰色,操作为:'已结束'.
        (4)当点击'停用'时,该行秒杀管理文字全部变为灰色,操作为:'已失效'.
        (5)未开始状态下,'编辑'的选项都可使用;进行中,只能编辑库存.
        (6))原型-新建秒杀中,商品名需要细分至具体的商品名,不能为整组秒杀(这里的商品名是指商品类目名还是单个商品名??)
        (7)原型-新建秒杀中,原价显示该商品所有的价格,为单选.
        (8)数据库表设计:
        *表名:seckill
        *各列分析:

     1 id(该条秒杀数据项的id,id=Column(Integer,nullable=False,primary_key=True))
     2 activity_id(秒杀活动id,不具有唯一性,可以有多行的activity_id相同,activity_id=Column(Integer,nullable=False);设定activity_id为当前秒杀的店铺id字符串加上当前秒杀活动的整形时间戳字符串)
     3 shop_id(该条秒杀所在的店铺的id,shop_id=Column(Integer,nullable=False))
     4 start_time(秒杀开始时间,时间戳类型,start_time=Column(Integer))
     5 end_time(秒杀结束时间,时间戳类型,end_time=Column(Integer))
     6 continue_time(秒杀持续的时间,时间戳类型,continue_time=Column(Integer))
     7 fruit_id(商品id,fruit_id = Column(Integer,ForeignKey(Fruit.id)))
     8 charge_type_id(当前秒杀商品的计价方式id,charge_type_id = Column(Integer,ForeignKey(ChargeType.id)))
     9 former_price(原价,浮点型,former_price=Column(Float))
    10 seckill_price(秒杀价,浮点型,计价方式与former_price相同,seckill_price=Column(Float))
    11 storage_piece(剩余库存换算成当前计价方式的份数,浮点型,storage_piece=Column(Float))
    12 activity_piece(活动库存的份数,浮点型,activity_piece=Column(Float))
    13 status(当前秒杀商品的状态,整形,status = Column(TINYINT);取值:-1(已停用),0(已结束),1(未开始),2(进行中))

        (9)'推广'链接跳到'发现'页面.
        (10)marketing表中新增加一个字段,控制店铺秒杀活动是否开启:seckill_active=Column(Integer,default=1) #0:关闭  1:开启
      7、 注意事项:
        (1)因为秒杀对时间的精度要求比较高,所以数据库中用时间戳存储和时间日期有关的数据.
      8、和时间戳有关的函数:
        ①时间戳转换成datetime格式的字符串:
          重写base.py中GlobalBaseHandler类中的timestamp_to_str方法(原方法中没有精确到秒):
          def timestamp_to_str(self, timestamp):
             return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(timestamp))
          或者使用该类中的通用工具函数code_to_text(column_name,code),调用时候column_name为'create_date_timestamp',code为整形的时间戳.
        ②获取当前时间的时间戳:now = datetime.datetime.now().timestamp()
        ③将时间戳转化为日期:
          b_time_stamp = 776361600
          dateArray = datetime.datetime.utcfromtimestamp(b_time_stamp)
          birthday = dateArray.strftime("%Y年%m月%d日")
          print(birthday)
          输出:1994年08月08日
        ④mysql把时间戳转化为日期:
          mysql>SELECT FROM_UNIXTIME( 1249488000, '%Y-%m-%d' ) 
          ->2007-11-20

    【8.12 周三】

      今日任务:商品秒杀
      1、 wdatepicker只显示时分秒
      2、 选中时候为绿色的方形单选按钮;switch开关按钮;
      3、 确认对话框的使用:
      4、 js查找离最近的具有某种属性的元素:

    1 $( document ).bind("click", function( e ) {
    2     $( e.target ).closest("li").toggleClass("hilight");
    3 });

      5、 jquery查找具有某种属性的元素的个数:
        $(".new-seckill-item").length
      6、 jquery not()方法:
        从包含所有段落的集合中删除 id 为 "selected" 的段落:
          $("p").not("#selected")
      7、 jquery可见性筛选:
        所有可见的表格:
          $("table:visible")
      8、 使用jquery prev()方法找到同级的前一个元素:
        $("p").prev(".selected")
      9、 jquery查找当前元素相对于第一个具有属性aaa的元素的index值:
        $(".aaa").index($this);
      10、 jquery获取第i个有属性aaa的元素:
        $(".aaa:eq(" + i+ ")");
      11、 datePicker最小时间要为当前时间
      12、 商品计价方式代码-汉字转换在base.py基类中有一个方法。
      13、 店铺推荐和默认分组这两个分组在goods_group表中是没有的,它们在group_priority表中:
          id = -1:推荐分组
          id = 0:默认分组
      14、 python将列表转换成字典:
          lista = [(-1, '推荐分组'), (0, '默认分组'), (22, '水果单品'), (25, '青年果汁'), (26, '青年果切')]
          lista = dict(lista)
        执行操作后,lista变为:
          {0: '默认分组', 25: '青年果汁', 26: '青年果切', 22: '水果单品', -1: '推荐分组'}
        注:字典可以以整数为下标。
      15、 将商品计价方式编码转换为计价方式文字显示(在base.py中)

     1 def getUnit(self,unit):
     2   if unit == 1:
     3    name =''
     4   elif unit == 2 :
     5    name =''
     6   elif unit == 3 :
     7    name =''
     8   elif unit == 4 :
     9    name ='kg'
    10   elif unit == 5 :
    11    name =''
    12   elif unit == 6 :
    13    name =''
    14   elif unit == 7 :
    15    name =''
    16   elif unit == 8 :
    17    name =''
    18   elif unit == 9 :
    19    name =''
    20   elif unit == 10 :
    21    name =''
    22   elif unit == 11 :
    23    name =''
    24   else:
    25    name =''
    26   return name

    【8.13 周四】

      今日任务:商品秒杀seckill
      1、 新建秒杀-后台数据获取:

     1 (1)先获取到当前店铺的group_id-group_name对照表
     2 (2)获取当前店铺的group_id-[(fruit_id,fruit_name),...]对照表
     3 (3)获取当前店铺的上架和下架的fruit_id表
     4 (4)遍历fruit_id表,查询fruit_id-storage对照表,只保留库存storage不为0的项。其中storage为一个有两个元素的列表,第一个元素为浮点型,表示库存的数目,第二个元素为字符串,
     5 
     6 表示库存的单位(如:斤,箱等)
     7 (5)取fruit_id-storage对照表的keys列表:fruit_id_usable_list,表示筛选出库存不为0的且为上架或下架的商品id
     8 (6)遍历fruit_id_usable_list表,从charge_type 表中查询:
     9 查询出fruit_id-[(price,num,unit,relate),..]对照表fruit_id_charge_type
    10 说明:假如苹果的总库存为storage=1000kg,苹果的其中一种计价方式为18元/3盒,每7盒14kg.
    11 那么这里的各个量的值分别为:price=18,num=3,self.getUnit(unit)='',relate=14/7=2(kg)
    12 那么当前库存对应的该计价方式的份数为:storage_piece=storage/relate/num = 1000/2/3 (份)
    13 把storage_piece append到fruit_id-[(price,num,unit,relate),..]对照表的每一项中。
    14 (7)整理传给前端的数据:
    15 ①group_id-group_name对照表
    16 ②group_fruit_dict对照表中删去库存为0的fruit项成为group_usable_fruit_dict
    17 ③fruit_id-storage对照表
    18 ④fruit_id-[(price,num,unit,relate,storage_piece),..]对照表

      2、 js中用eval只能把python的列表转换为obj,不能把字典转换成obj.
        所以如果后台python通过get方法传给页面,然后js从页面相应元素读取的是一个字典,那么需要先在python中把这个列表外面套一个字典才行。
      3、 jquery输入框获得焦点事件:$("p").focus(); 或$("p").focus(fn)
        失去焦点事件:$("p").blur(); 或$("p").blur(fn)
      4、 jquery判断正数的正则表达式:
        var reg = /^[1-9]d*.d*|0.d*[1-9]d*$/;
        console.log(reg.test('2.333'));
        输出:true
      5、 jquery dom元素遍历:
        jQuery中用$()方法获取的dom元素都会返回数组 并且jQuery中有隐形迭代,有些操作你无须去刻意循环它们,例如:$("div").text("hello");那么就会在所有的DIV中都加上hello.   若果硬要循环它们以进行某些处理 你可以使用jQuery提供的$.each()方法,或者这样写:

    1 $("div").each(function(){
    2     //这里干你想干的事情 
    3     //用$(this)可以访问正在循环的元素 
    4 });

      6、 必须设置距离现在最少五分钟以后秒杀才能开始
      7、 jquery的return只能return出一层方法,如果有多个方法嵌套,内层return,则要设置stop-flag变量来return.
      8、 数据中少一个计价方式id.
      9、 *各列分析:

     1 1.id(该条秒杀数据项的id,id=Column(Integer,nullable=False,primary_key=True,autoincrement=True))
     2 2.activity_id(秒杀活动id,不具有唯一性,可以有多行的activity_id相同,activity_id=Column(Integer,nullable=False);设定activity_id为当前秒杀的店铺id字符串加上当前秒杀活动
     3 
     4 的整形时间戳字符串)
     5 3.shop_id(该条秒杀所在的店铺的id,shop_id=Column(Integer,nullable=False))
     6 4.start_time(秒杀开始时间,时间戳类型,start_time=Column(Integer,nullable=False))
     7 5.end_time(秒杀结束时间,时间戳类型,end_time=Column(Integer))
     8 6.continue_time(秒杀持续的时间,时间戳类型,continue_time=Column(Integer))
     9 7.fruit_id(商品id,fruit_id = Column(Integer,ForeignKey(Fruit.id,nullable=False)))
    10 8.charge_type_id(当前秒杀商品的计价方式id,charge_type_id = Column(Integer,ForeignKey(ChargeType.id),nullable=False,))
    11 9.former_price(原价,浮点型,former_price=Column(Float))
    12 10.seckill_price(秒杀价,浮点型,计价方式与former_price相同,seckill_price=Column(Float,nullable=False))
    13 11.storage_piece(当前商品剩余库存换算成当前计价方式的份数,取整,整形,storage_piece=Column(Integer))
    14 12.activity_piece(活动库存的份数,整形,activity_piece=Column(Integer))
    15 
    16 13.activity_status(当前秒杀活动的状态,整形,activity_status = Column(TINYINT,default=1,nullable=False);取值:-1(已停用),0(已结束),1(未开始),2(进行中))
    17 14.not_pick(未领取的商品的份数,默认等于当前活动库存的份数,整形,not_pick=Column(Integer,default=0))
    18 15.picked(已经领取的商品的份数,默认为0,整形,picked=Column(Integer,default=0))
    19 16.ordered(已经下单的商品的份数,默认为0,整形,ordered=Column(Integer,default=0))
    20 17.deleted(已经被从该秒杀活动中删除的商品的份数,默认为0,整形,deleted=Column(Integer,default=0))

      10、 js取字符串的子串:
          str = str.substr(start,lenth)(结果不包括下标为start+lenth的字符)
      11、 python把字符串转换为时间戳:
            start_time = '2015-08-12 12:34:33'
            start_time = int(time.mktime(time.strptime(start_time,'%Y-%m-%d %H:%M:%S')))
          将时间戳转化为localtime字符串:
            x = time.localtime(1317091800.0)
            time.strftime('%Y-%m-%d %H:%M:%S',x)
            >>2011-09-27 10:50:00

    【8.14 周五】

      今日任务:商品秒杀
      1、 数据库建表:
        

     1 (1)秒杀商品表
     2 create table seckill_goods(
     3  id int(11) not null primary key auto_increment,
     4  fruit_id int(11) not null,
     5  activity_id bigint(20) not null,
     6 
     7  charge_type_id int(11) not null,
     8  former_price float,
     9  seckill_price float not null,
    10  
    11  storage_piece int(11),
    12  activity_piece int(11),
    13 
    14  not_pick int(11),
    15  picked int(11) default 0,
    16  ordered int(11) default 0,
    17  deleted int(11) default 0,
    18  status tinyint(4) default 1,
    19  
    20  foreign key(activity_id) references seckill_activity(id),
    21  foreign key(fruit_id) references fruit(id),
    22  foreign key(charge_type_id) references charge_type(id)
    23 );
    24 
    25 (2)秒杀活动表:
    26 create table seckill_activity(
    27  +++id int(11) not null primary key auto_increment,
    28  ---id bigint(20) not null,
    29  shop_id int(11) not null,
    30  start_time int(11) not null,
    31  end_time int(11) not null,
    32  continue_time int(11) not null,
    33  
    34  activity_status tinyint(4),
    35 
    36  foreign key(shop_id) references shop(id)
    37 );
    38 
    39 (3)加字段:
    40 marketing表中新增加一个字段,控制店铺秒杀活动是否开启:seckill_active=Column(TINYINT,default=1) #0:关闭  1:开启
    41 alter table marketing add column seckill_active tinyint(4) default 0;

      2、 python延时函数:time.sleep(10),延时单位:秒

    1 import time
    2 print "a"
    3 time.sleep(5)
    4 print "b"
    5 time.sleep(10)

        程序会先输出“a”,暂停5秒后再输出“b”


    【8.18 周二】

      今日任务:商品秒杀
      1、 tornado在html中给一个新变量赋值要用set:
        {% set data_item = output_data[0] %}
      2、 让html input不可编辑的方法:
        方法1: onfocus=this.blur() 当鼠标放不上就离开焦点
            <input type="text" name="input1" value="中国" onfocus=this.blur()>
        方法2:readonly
            <input type="text" name="input1" value="中国" readonly>
            <input type="text" name="input1" value="中国" readonly="true">
        方法3: disabled
            <input type="text" name="input1" value="中国" disabled="true">
      3、 seckill_goods表新增加一个字段:
        status tinyint(4) default 1
      4、 二维码相关外部js:
        <script src="/static/js/third/qrcode.min.js"></script>
      5、 jquery如何click,2个事件切换:
        在click外面设置一个数值变量初始为0,每次click触发后增加1,然后让该变量对2取余,不是就得到两个不同的状态了,然后根据这个值写函数就行了
      6、 jquery实现点击一个div外部的任意地方隐藏这个div(假设其class为sw-er-tip)的方法:
        假设div的外部是body,则先为body添加点击事件:

    1 $(document).on("click",function(e){
    2  if($(e.target).closest(".sw-er-tip").size()==0){
    3   $(".sw-er-tip").addClass("invisible");
    4  }
    5 });  

        然后为这个div添加点击事件并停止冒泡:

    1 $(document).ready(function(){
    2  ...
    3 )}.on("click",".spread-activity",function(e){
    4  e.stopPropagation();
    5  $(".sw-er-tip").addClass("invisible");
    6      $(this).closest(".operate").children(".sw-er-tip").removeClass("invisible");  //operate为sw-er-tip的外层父元素
    7 });

      7、 github一次add所有文件:
        sudo git add . --ignore-removal
      8、 python把当前时间转换为时间戳:
        now_date=int(time.time())
      9、 当前如果有正在秒杀的活动,那么秒杀功能不能被关闭。
      10、 Ubuntu 14.04 用户可以通过以下命令为chrome安装 Pepper Flash Player For Chromium :
          sudo apt-get install pepperflashplugin-nonfree
          sudo update-pepperflashplugin-nonfree --install

    【8.19 周三】

      今日任务:商品秒杀商城端后台
      1、 修改表结构重新建表:

     1 秒杀活动表:
     2 create table seckill_activity(
     3  id int(11) not null primary key auto_increment,
     4  shop_id int(11) not null,
     5  start_time int(11) not null,
     6  end_time int(11) not null,
     7  continue_time int(11) not null,
     8  
     9  activity_status tinyint(4),
    10 
    11  foreign key(shop_id) references shop(id)
    12 );
    13 
    14 秒杀商品表
    15 create table seckill_goods(
    16  id int(11) not null primary key auto_increment,
    17  fruit_id int(11) not null,
    18  activity_id int(11) not null,
    19 
    20  charge_type_id int(11) not null,
    21  former_price float,
    22  seckill_price float not null,
    23  
    24  storage_piece int(11),
    25  activity_piece int(11),
    26 
    27  not_pick int(11),
    28  picked int(11) default 0,
    29  ordered int(11) default 0,
    30  deleted int(11) default 0,
    31  status tinyint(4) default 1,
    32  
    33  foreign key(activity_id) references seckill_activity(id),
    34  foreign key(fruit_id) references fruit(id),
    35  foreign key(charge_type_id) references charge_type(id)
    36 );

      2、 github忽略本地工作区删除的文件:
         git add --ignore-removal <文件路径路径>',忽略本地工作区中移除的文件。
      3、 fruit表新增加字段:(一种商品只能同时参与一种活动,活动在同一商品上不能叠加)

    1 activity_status = Column(TINYINT,default=0)  #0(该商品未参与任何活动),1(参与秒杀活动),2(参与限时折扣),...(等待扩展中)
    2 seckill_charge_type = Column(Integer,default=0) #秒杀活动中该商品所使用的计价方式id
    3 alter table fruit add column activity_status tinyint(4) default 0;
    4 alter table fruit add column seckill_charge_type int(11) default 0; 

      4、 如果24小时之内没有秒杀活动,那么发现页面的秒杀活动不显示。

    1 sec_fruit = self.session.query(models.Fruit).filter_by(id = fruit_id).with_lockmode('update').first()
    2     sec_fruit.activity_status = 1
    3     sec_fruit.seckill_charge_type = charge_type_id

      5、 秒杀活动编辑限定:

        *要在base的类中为秒杀活动增加全局刷新函数(店铺管理后台,商城后台,customer后台)
        还没有开始的秒杀活动:所有信息都可以编辑
        正在进行中的秒杀活动:
          只能编辑活动的持续时间(而且只能比原来增加)、每种商品的活动库存(只能比原来的增加),正在进行的秒杀活动不能添加新商品,也不能删除现有的商品。
      6、 /seckill/(w+)页面数据分析:
        需要:
        [[活动id,秒杀活动开始时间,持续时间,[[商品秒杀id,商品fruit_id,商品img_url,商品名称,商品计价方式id,商品计价方式文字,商品秒杀价省钱数,商品活动库存],..]],...]
      7、 sqlalchemy中filter和filter_by可以连着用,如:
        aa = self.session.query(models.Fruit).filter_by(shop_id = 4).filter(models.Fruit.id < 500).all()
        print(len(aa))


    【8.20 周四】

      今日任务:商品秒杀商城端
      1、 python中的字典直接传给js,js不能解析成对象,必须在python中为字典外面套上一个列表才行。
      2、 sqlalchemy中预提交:
        session.flush()(和commit类似)
        如果预提交失败则会回滚
      3、 全局实时更新店铺秒杀活动基类方法:

     1 def update_seckill_base(self,shop_id):
     2   current_shop_id = shop_id
     3   notstart_list = self.session.query(models.SeckillActivity).filter_by(shop_id = current_shop_id,activity_status = 1).all()
     4   for item in notstart_list:
     5    now_time = int(time.time())
     6    if item.start_time <= now_time and item.end_time > now_time:
     7     item.activity_status = 2
     8     self.session.flush()
     9    elif item.start_time <= now_time and item.end_time <= now_time:
    10     item.activity_status = 0
    11     self.session.flush()
    12   self.session.commit()
    13 
    14   killing_list = self.session.query(models.SeckillActivity).filter_by(shop_id = current_shop_id,activity_status = 2).all()
    15   for item in killing_list:
    16    now_time = int(time.time())
    17    if item.end_time <= now_time:
    18     item.activity_status = 0
    19     self.session.flush()
    20   self.session.commit()
    21   return None

      4、 python字节码
      5、 刷新数据库店铺秒杀活动信息
      6、 js中用hide和show方法的时候容易导致元素显示异常,这种情况下就要改用addClass('hidden')和removeClass('hidden')
      7、 python中字符串split,如果字符串中不包含split中的分隔符,那么就会返回整个字符串,不会报错。
      8、 商城商品首页商品列表秒杀商品需要数据:
        先判断当前店铺当前时刻是否有秒杀活动(activity_status=2),如果有,则先返回给前台一个秒杀活动标志变量,秒杀img_url:
        has_seckill_activity:1,seckill_img_url:xxx(这个url只对应一个图片,是存放在七牛上写死的图片链接)
        遍历商品列表的时候,先通过fruit表的activity_status字段判断每个商品是否是秒杀商品(fruit表的activity_status字段对应的秒杀商品状态码),如果是,把返回数据的is_seckill赋值为1
        [is_seckill:1,activity_id:xxx,seckill_goods_id:xxx,***fruit_id:xxx,charge_type_id:xxx,***img_url:xxx(只要个),***fruit_name:xxx,charge_type_text: xxx,price_dif:xxx,activity_piece:xxx]
      9、 ?要加一个限制:新建秒杀活动的时候,不能新建和当前seckill_goods表中所有activity_status=1或2(未开始秒杀和正在进行的秒杀)的fruit_id相同的商品.
    ?还要加一个限制:同一个秒杀活动中同一个商品只能出现一次。
      10、 在商城首页公告表notice表中加入四个字段,用于存放活动的公告图片url(目前url暂时是写死的):
          http://7rf3aw.com2.z0.glb.qiniucdn.com/o_19t7mu3281ms2fnd51q17hsqqo7   团购
          http://7rf3aw.com2.z0.glb.qiniucdn.com/o_19t7mvj70f7dn221sd1pfn18l2d   折扣
          http://7rf3aw.com2.z0.glb.qiniucdn.com/o_19t7n0emj17d71ml9lftn6u1cocj  预售
          http://7rf3aw.com2.z0.glb.qiniucdn.com/o_19t7n14fh1c0s1g0hne1gu45jhp   秒杀

    1 seckill_img_url = Column(String(100)) #秒杀公告背景
    2 gbuy_img_url = Column(String(100)) #团购公告背景
    3 dscount_img_url = Column(String(100)) #折扣公告背景
    4 presell_img_url = Column(String(100)) #预售公告背景

        注:notice表中config_id就是shop_id
        字段数据添加时机:只要店铺开启某项活动,就把该店铺的notice表中对应的url字段赋值为七牛云上的图片url.
      10、 商城商品首页商品列表后台返回数据中增加判断字段:is_activity(0:当前无活动,1:秒杀,2:xxx,...)
      11、 ?秒杀商品表要加一个已经抢过该商品的顾客的customer_id字段,以保证同一个活动中同一件商品每个人只能抢一份(bought_customer)
          bought_customer = Column(String(5000))
          alter table seckill_goods add column bought_customer varchar(5000);
          经过思考后,发现这种方法不太合理,而且查询起来比较复杂,所以改变方案:新建一个customer_seckill_goods表(用户抢购的秒杀商品表):

     1 create table customer_seckill_goods(
     2  id int(11) not null primary key auto_increment,
     3  customer_id int(11) not null,
     4  shop_id int(11) not null,
     5  seckill_goods_id int(11) not null,
     6  status tinyint(4) default 0, 
     7 
     8  foreign key(customer_id) references customer(id),
     9  foreign key(shop_id) references shop(id),
    10  foreign key(seckill_goods_id) references seckill_goods(id)
    11 );

      12、 用户抢购的秒杀商品表

    1 class CustomerSeckillGoods(MapBase, _CommonApi):
    2  __tablename__='customer_seckill_goods'
    3  id = Column(Integer,nullable=False,primary_key=True,autoincrement=True)
    4  customer_id = Column(Integer,ForeignKey(Customer.id),nullable=False)
    5  shop_id = Column(Integer,ForeignKey(Shop.id),nullable=False)
    6  seckill_goods_id = Column(Integer,ForeignKey(SeckillGoods.id),nullable=False)
    7  status = Column(TINYINT,default=0)

      13、 postJson可以向别的页面发请求,只用把url改成其他页面即可。
      14、 删除表中的一列:
          alter table AAA drop column aName;

    【8.21 周五】

      今日任务:商品秒杀商城首页
      1、 在sqlalchemy的py文件中新建数据库表class之后,运行系统会自动在数据库中新建相应的表,而往已有的表中新插入字段则需要手动往数据库中插入,并不能自动插入。
      2、 mysql自动增长的id怎么知道刚插入的数据的id是多少?
        解决方法:
        ①先新建要插入的记录:
        (假设Fruit表的id是自动增长的)
          record = models.Fruit(name='aaa',price=12.3)
        ②add记录:
          self.session.add(record)
        ③flush:
          self.session.flush()
        ④insert_id = record.id
        ⑤commit:
          self.session.commit()
        ⑥再用第④步取得到的insert_id做其他事情。
      3、 遍历fruit_id_usable_list表,从charge_type 表中查询:
        查询出fruit_id-[(price,num,unit,relate),..]对照表fruit_id_charge_type
        说明:假如苹果的总库存为storage=1000kg,苹果的其中一种计价方式为18元/3盒,每7盒14kg.
             那么这里的各个量的值分别为:price=18,num=3,self.getUnit(unit)='盒',relate=14/7=2(kg)
           那么当前库存对应的该计价方式的份数为:storage_piece=storage/relate/num = 1000/2/3 (份)
        把storage_piece append到fruit_id-[(price,num,unit,relate),..]对照表的每一项中。
      4、 在处理秒杀商品的计价方式的时候遇到了较复杂的问题,讨论了一下午:
        如果不将秒杀商品的计价方式作为一种新的计价方式插入计价方式表中,那么在生成订单和统计数据的时候将会遇到不方便和数据不完整的问题;如果将秒杀商品的计价方式作为一种新的计价方式插入计价方式表中,那么很多和计价方式相关的地方都要相应的改变,工作量很大。
    最后权衡利弊之后,决定这样处理:
        在计价方式表中新增一个字段activity_type = Column(TINYINT,default=0),用于表示当前这条计价方式对应的活动类型,默认值是0,表示当前计价方式不属于任何活动,其他值:1(秒杀活动新建的计价方式),2(限时折扣活动新建的计价方式),...
        然后在每次新建或者编辑秒杀商品信息后,都向计价方式表中插入该秒杀商品对应的计价方式记录,并把生成的计价方式id插入到秒杀商品表中。在抢购和下单的时候,都以这个计价方式为依据进行处理。
        最后再在所有有对计价方式进行遍历的地方判断计价方式的activity_type,筛选出来需要的计价方式。
      5、 python列表删除符合条件的元素:

     1 >>> a = [1,2,3,4]
     2 >>> list(filter(lambda e:e!=1,a))
     3 [2, 3, 4]
     4 >>> a
     5 [1, 2, 3, 4]
     6 
     7 >>> a = [{'a':111,'b':345},{'a':23,'b':3}]
     8 >>> a
     9 [{'b': 345, 'a': 111}, {'b': 3, 'a': 23}]
    10 >>> a = list(filter(lambda item:item['a'] != 111,a))
    11 >>> a
    12 [{'b': 3, 'a': 23}]
    13 
    14 或者:
    15 a = [e for e in a if e!=1]
    16 a = [e for e in a if e['a'] != 111]

      6、 jquery中each方法中发ajax请求时候,可能会由于异步的原因导致先执行后面的代码再执行请求导致逻辑错乱,为了避免这个问题,需要在发ajax请求之前关闭异步:

    1 $.ajaxSetup({
    2  async : false
    3 });

      7、 linux截屏软件:
        sudo apt-get install scrot
        然后命令行输入:scrot -s可以用鼠标选区然后保存截图到当前目录。 

    【8.24 周一】
      1、 今日任务:商品秒杀商城端支付环节
      2、 要加一个限制:同一个秒杀活动中不能存在两种相同的商品。
      3、 jquery中遍历某个类的所有元素的两种方法:

    1for(var i=0; i<$('.aaa').size;i++){
    2  var $this = $('.aaa').eq(i);
    3  function($this){
    4   ... 
    5  }
    6 }
    1 ②$('.aaa').each(function(){
    2  ...
    3 }

        注:用each方法遍历如果each中有ajax函数,那么要在执行ajax函数前关闭异步,否则会导致执行时候逻辑错乱的问题。
      4、 关闭ajax异步:

    1 $.ajaxSetup({
    2  async : false
    3 });

      5、 对于charge_type表的分析:

    1 price unit num unit_num active market_price select_num relate
    2 50      8    2      7        1         48            19         2.714
    3 转换成'每份'的计价方式:
    4 50      3    1     7/2       1         48            19    19/(7/2)(=2.714*2)

      6、 seckill_goods表加一个字段:
        seckill_charge_type_id = Column(Integer,ForeignKey(ChargeType.id),nullable=False)  #当前秒杀商品的计价方式id
        alter table seckill_goods add column seckill_charge_type_id int(11) not null references

        charge_type(id);
      7、 charge_type表新增加一个字段:
        activity_type = activity_type = Column(TINYINT,default=0) #0:正常计价方式,未参与任何活动;1:秒杀活动计价方式;2:限时折扣活动计价方式
        alter table charge_type add column activity_type tinyint(4) default 0;
        注:每次新建或编辑秒杀活动后都往charge_type表中插入秒杀活动新建的计价方式。
      8、 m-admin/js/util.js里面有各种工具方法。
      9、 商品如果正在参加秒杀活动,那么不允许下架(在商品下架接口中要加以判断)

     1    end_charge_type = [x for x in charge_types if x.activity_type == -1]
     2    end_charge_type_id = []
     3    for item in end_charge_type:
     4     end_charge_type_id.append(item.id)
     5    seckill_goods_list = self.session.query
     6 
     7 (models.SeckillGoods).filter(models.SeckillGoods.seckill_charge_type_id.in_
     8 
     9 (end_charge_type_id)).with_lockmode('update').all()
    10    for item in seckill_goods_list:
    11     item.picked -= 1
    12     item.not_pick += 1
    13    self.session.commit()

      10、 店铺商品管理中,商品详细信息是在base.py的getGoodsData方法中查询到的。

      11、 用户在购物车中删除秒杀商品后要有相应的响应。

      12、 mysqldump -uroot -psenguo senguocc > senguocc2015-8-24.sql

    【8.25 周二】
      今日任务:商品秒杀商城端支付环节
      1、 今天遇到好几次python3的错误:'段错误(核心已转储)'
      2、 seckill_goods表的activity_piece,not_pick,picked,ordered四个字段之间的关系:
        商品首页和秒杀的发现页面显示的秒杀商品的库存为activity_piece,只有当一个商品被下单成功后该库存才减去1;当一个商品被用户放到购物车里时,not_pick减1,picked加1,另外两个不变;当一个正在参与秒杀的商品被用户从购物车里删除时,not_pick加1,picked减1;当一个秒杀商品被用户下单成功后,activity_piece减1.
      3、 加载商品首页时遇到如下警告,加载速度非常慢,达到一分钟多:

     1 /usr/local/lib/python3.4/dist-packages/sqlalchemy/sql/default_comparator.py:153: SAWarning: 
     2 
     3 The IN-predicate on "charge_type.id" was invoked with an empty sequence. This results in a 
     4 
     5 contradiction, which nonetheless can be expensive to evaluate.  Consider alternative 
     6 
     7 strategies for improved performance.
     8   'strategies for improved performance.' % expr)
     9 /usr/local/lib/python3.4/dist-packages/sqlalchemy/sql/default_comparator.py:153: SAWarning: 
    10 
    11 The IN-predicate on "seckill_goods.seckill_charge_type_id" was invoked with an empty 
    12 
    13 sequence. This results in a contradiction, which nonetheless can be expensive to evaluate.  
    14 
    15 Consider alternative strategies for improved performance.
    16   'strategies for improved performance.' % expr)
    17 [I 150825 18:39:40 web:1811] 200 GET /jyjfruit (127.0.0.1) 73.69ms
    18 /usr/local/lib/python3.4/dist-packages/sqlalchemy/sql/default_comparator.py:153: SAWarning: 
    19 
    20 The IN-predicate on "seckill_goods.activity_id" was invoked with an empty sequence. This 
    21 
    22 results in a contradiction, which nonetheless can be expensive to evaluate.  Consider 
    23 
    24 alternative strategies for improved performance.

        后来重新导入数据库,换了一家店铺进行测试,问题解决。
      3、 新建秒杀时要判断各个商品是否在同一时间段的其他商品已经存在,如果存在,则不能进行新建。

    【8.26 周三】
      今日任务:商品秒杀商城端支付环节
      1、 点击购物车的'提交订单'后,js向后台发post请求的参数中,fruits参数的形式为:
         {'12668': 2, '12641': 3, '12645': 1},表示计价方式和数量的键值对字典;
        在该post请求的一开始要更新秒杀数据,然后设置一个overdue变量,初始化为0,然后遍历fruits列表的计价方式判断有没有商品的活动过期,如果有商品的活动过则置overdue为1,直接self.send_success(overdue=overdue),前台根据overdue的值重定向到当前购物车页面,并给用户发出提示,刷新购物车

    页面,重新进行处理。
      2、 问题:编辑秒杀活动时,在判定选择商品是否与该时间段其他活动的商品重复时,会出现'与当前活动秒杀商品重复'的错误。
      3、 对于余额支付和货到付款的订单,商家删除订单后,会恢复库存;在线支付的还不能删除。
      4、 在商城端显示秒杀商品的时候,要先判定该秒杀商品的ordered值是否等于activity_piece,如果相等,则表明已经被抢光了,不予显示。
      5、 在线支付是有15分钟的付款时间的,如果15分钟内未付款,那么订单将会自动取消,返还库存。
        对于秒杀活动的在线支付这样处理:
          给用户15分钟的在线支付时间,在这期间只要完成在线支付即可,不用管活动是否到期。15分钟以后自动取消订单。
      6、 秒杀商品和限时折扣商品的去重问题:同一时间段内同一商品不能参加不同的活动,也不能参加同一种活动在同一时间段内的不同场次。
    因为限时折扣涉及到周期性活动的问题,所以去重比较复杂。而且在与循环时间比较的时候还要注意python的时区问题。
        *思路:在全局基类中为秒杀活动和限时折扣活动分别写judge_seckill和judge_discount方法,其他活动可以调用这两个方法判断其他活动将要新建的商品是否在这两个活动里面,参数基本形式如下:def judge_seckill(self,fruit_id,time_way,start_time,end_time,f_time,t_time,weeks)

        *参数分析:其中time_way表示参数时间的形式,time_way=0表示参数时间为start_time和end_time,非周期时间,此时忽略f_time,t_time,weeks三个参数;若time_way=1则表示参数时间为f_time和t_time以及weeks,这为周期时间,此时忽略start_time和end_time两个参数。其中weeks为一个整型列表,元素为代表周几的整数(取值1-7),f_time表示每天的开始时间(是一个整型的秒数,介于0到24小时之间),t_time表示每天的结束时间(是一个整型的秒数,介于0到24小时之间)返回值:True(传入参数fruit_id对应的商品和当前已经建立的秒杀活动时间上无冲突);False(传入参数fruit_id对应的商品和当前已经建立的秒杀活动时间上有冲突)
      7、 获取星期数:

    1 >>> x = time.localtime(1317091800.0)
    2 >>> time.strftime('%w',x)
    3 '2'
    4 >>> time.strftime('%Y-%m-%d %H:%M:%S',x)
    5 '2011-09-27 10:50:00'

    【8.27 周四】
      今日任务:已经完成商品秒杀从商城端到店铺后台端的全部功能,接下来的两天主要是全方位debug.
      1、 python延时执行函数的方法:
        work = Timer(60*15,func,(param1,param2,))
        work.start()
      2、 为order表新增字段activity_type,类型为键值对字符串,键是计价方式,值是计价方式对应的活动名称,用于存储该订单中每种计价方式id对应的水果参与的活动名称,如果值为空字符串,则表示未参与任何活动;如果值为非空,则表示参与了值字符串所表示的活动。
        alter table senguocc.order add column fruit_activity varchar(1000);
        fruit_activity = Column(String(1000))
        后来考虑不用新加字段,只用利用现有的fruits字段即可。
      3、 在订单详情显示中,每种商品名称前都添加参与的活动名称,如果未参与任何活动则不添加,类似这样:
        {% if 'activity_name' in list(fruits[key].keys()) and fruits[key]['activity_name'] %}({{fruits[key]['activity_name']}}){% end %}{{fruits[key]['fruit_name']}}
      4、 access_token表存微信的认证信息,用于发模板消息。如果发现发送失败,那么先清空这个表再试试。
        delete from access_token;
    【8.28】

      今日任务:商品秒杀全方位debug.
      森果实习最后一天
      1、 为所有包含折扣商品的订单的fruits字段添加折扣数(0~1之间),非折扣商品的折扣数为1,显示订

    单商品详情的时候折扣商品要显示原价乘上折扣数的价格。

     1 discount_rate
     2 {% if 'discount_rate' in list(fruits[key].keys()) %}{{round(float(price1)*fruits[key]
     3 
     4 ['discount_rate']*fruits[key]['num'], 2)}}{% else %}{{round(float(price1)*fruits[key]
     5 
     6 ['num'], 2)}}{% end %}
     7 
     8 {% if 'discount_rate' in list(fruits[charge_type.id].keys()) %}{{round(float(price1)
     9 
    10 *fruits[charge_type.id]['discount_rate']*fruits[charge_type.id]['num'], 2)}}{% else %}
    11 
    12 {{round(float(price1)*fruits[charge_type.id]['num'], 2)}}{% end %}

      2、 artTemplate中if语句用法:{{ if a == 1 }}...{{else}}...{{/if}},和tornado中不同。
      3、 测试服务器测试报告:
        1.新建秒杀页面商品信息卡的overflow:auto属性丢失,要加上。(ok)
        2.店铺后台商品管理添加计价方式后,会将之前的计价方式全部成对显示。(ok)
        解决:admin.py 2666行q_charge = self.session.query(models.ChargeType).filter_by(id=charge_type['id']).filter(models.ChargeType.activity_type.in_([0,-2]))一句中_in后面少了小括号,加上即可.
        3.代码推到线上的时候,要改动的地方:
        ①店铺后台秒杀管理的'点击查看使用教程'的链接要改成bbs中正确的秒杀教程url链接。
        ②店铺后台秒杀管理的'推广链接'要改成线上的url.(ok)

    ******************************************************************************************************************end

      至此,为期三个月的森果实习结束,感受颇多,成长颇多,感受及总结将会陆续总结在其他博文中。感谢森果。

  • 相关阅读:
    MVC通过后台注解来添加对数据的验证。
    HTML赋值方法练习
    HTML辅助方法的练习一
    第一次接触MVC Models概念
    部分视图的理解
    使用布局文件(Layout)
    springboot基本配置及快速启动
    springboot代码测试注意事项
    logback日志的基本使用
    springboot快速创建项目框架
  • 原文地址:https://www.cnblogs.com/jiayongji/p/4534830.html
Copyright © 2020-2023  润新知