分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019 2020 2021

技术文章列表

  • 基于SSM的超市订单管理系统

    1 系统需求分析超市订单管理系统是一个专为连锁店、超市等商业场所提供订单管理平台的系统。该系统的目标是建立一个订单管理平台,为需要合理规划超市供应链、供应商以及工作人员提供的便捷的平台。该系统的主要业务需求包括记录并维护某超市的供应商信息,以及该超市与供应商之间的交易订单信息,包括三种角色,系统管理员经理,普通员工。
    1.1 系统功能分析本系统主要的功能是实现超市订单管理功能,以便为超市、连锁店提供以及其他负责人提供订单详情、联系方式等,系统的主要功能有以下五个方面:

    登录/注销:管理员可以在网站上登录浏览,离开时注销并退出
    订单管理:管理员可以浏览所有订单信息,并且通过点击查看了解订单详情信息
    供应商管理:管理员可以在网站浏览所有供应商信息,并在在与其他供应商达成合作之后,添加相关供应商信息,并且通过点击查看了解他们的联系方式等
    用户管理:管理员可以管理所有超市员工用户,对用户进行增删改查,对于离职或其他原因的未工作用户给予注销管理
    密码修改:管理员可对自己的账号密码进行修改,填写对应之前的正确密码以及新密码之后,即完成相关修改密码操作
    搜索功能:在以上管理界面中,均允许了管理员根据关键字进行搜索,要求搜索框中输入的字段必须完全包含在物品名称中,否则无法查询


    1.2 系统功能需求根据系统功能要求,该超市订单管理系统以管理员为中心的用户角色,可以将系统分解成几个模块来分别设计应用程序界面,如图 1.1所示。

    1.3 系统性能需求超市订单管理系统的开发是在Window10平台上,以SSM为架构,采用MySQL 作为数据库管理系统管理后台数据库。本系统是超市信息管理建设中必不可少的一部分,它实现了现代管理信息系统的大部分功能需要。使用本系统可以使超市管理更加方便快捷,合理的页面设计也使得这个用户充分享受到基于Internet管理信息系统的优越。本系统开发说明:
    1.3.1 功能完备在开发初期,查看了大量关于电子商务,管理信息系统,J2EE等方面的资料,同时借鉴了很多其他电子商务网站和管理信息的流程。经过总结,确定了满足需求分析的基本模块。系统总体设计上实现了整个系统模块的划分,系统主要包含5大模块,分别是:订单管理信息,供应商管理,用户管理,修改密码,登陆退出系统,基本上实现了综合管理系统的所有功能。 
    1.3.2 界面友好系统用户登陆到管理页面后,每页有导航和引领的作用。系统具有自适应的能力,同时导航条方便快捷的引导用户进行各种合理的操作。
    1.3.3 管理科学本系统一开始就从管理学的角度做出了详细细致的考虑,后来有参考了电子商务管理等,最后才做出了系统总体设计,因此可以讲该系统是较为科学的。
    系统的性能需求主要表现在数据库中的各个表需要频繁地被插入、删除以及更新。对于用户来说,系统地响应时间不宜太长,否则会降低用户体验。为此要求我们建立良好的表结构,加上足够的存储空间以及硬件性能。
    2 可行性分析2.1 研究前提随着我国经济情况的日新月异,飞速发展,涌现出许许多多的超市和便利店。越来越多的人喜欢到超市购物,超市里销售的商品也呈现出多种多样的变化趋势。我们开发一个超市订单管理系统,它可以对仓储各环节实施全过程控制管理,对整个进货、退货、盘点等各个环节的规范化作业,控制整个过程的正常运行。去掉了手工书写票据和送到机房输入的步骤,解决库房信息陈旧滞后的弊病,方便了仓库管理人员对物品的放置和调配,提高了工作效率。
    该系统容易被接受,具有简单易学性,便于管理等功能,是对超市订单管理的一种有效工具。
    2.2 设计要求2.2.1 安全性超市订单管理增强对产品规范的审计,重点确定该项目中需要审计的产品。买家只能针对卖家允许公开的信息进行查阅。买家只享受对自己账号内数据的查阅权,与定后处理权,订货支付权,申请退货权,不允许偷窥其他人。卖家只能针对买家允许公开的信息进行查阅。卖家只享受对自己账号内数据的查阅权,发货权,退款相应处理权,不允许偷窥其他人。
    2.2.2 系统性能管理员登录查看超市供应商与超市员工用户管理,可以进行增、删、改、查等操作。超市订单系统可以使超市的管理趋于正规化、现代化和系统化。本项目的产品可以达到以下目标:

    提高工作效率,减少返工
    业务流程的流水线化
    符合相关标准和规则
    与目前的应用产品相比较,提高了可用性或减少了失效程度

    2.2.3 可扩展性所有信息呈现,操作完全由打开的网页呈现并完成。本系统所占有的是超市市场,它追求的是简单、易学、易用,能够更好地解决管理人员的负担,能够辅助超市有效的管理物品。对于订单管理系统的用户,可满足对订单管理的需求,且此种需求被接受并且满足,其系统便可以推广。

    3 数据库设计3.1 数据库需求分析经过对超市管理系统的调查分析,得出用户的需求大致如下:

    管理员可以在系统中对订单、供应商以及用户进行增、删、改、查的处理
    管理员需要输入账号密码登录,并且可以增添新的管理员

    如下是利用数据流图方法对数据库做需求分析:
    第一步:由用户的需求,可以得到顶层数据流图如图3.1.1所示。

    第二步:超市订单管理系统的第1层数据流图如图3.1.2所示。

    第三步:超市订单管理系统的第2层数据库流图——订单管理的细化数据流图如图3.1.3所示。

    第四步:超市订单管理系统的第2层数据流库——供应商管理的细化数据流图如图3.1.4所示。

    第五步:超市订单管理系统的第2层数据流库——用户管理的细化数据流图如图3.1.5所示。

    根据如上的数据流程图,可以列出以下记录超市订单管理所需的数据项和数据结构:

    管理员:管理员ID、管理员姓名、管理员密码、管理员性别、管理员角色、管理员出生日期、管理员电话、管理员住址
    订单:订单编码、商品名称、供应商名称、订单金额、是否付款
    供应商:供应商编码、供应商名称、联系人、联系电话、微信

    3.2 数据库概念结构设计本系统一共有用户、供应商、订单、角色、地址这五个基本实体。
    管理员可以对应多个订单,而一个订单只能对应于一个管理员。管理员可以管理多个供应商,而一个供应商只能对应于一个管理员。一个供应商可以对应多条订单,但一条订单只能对应于一个供应商。此外,有一个用户对应一个角色,一个角色对应多个用户;一个地址对应多个订单,一个订单对应一个地址。数据库表之间的关系如下:


    用户:主键ID、用户编码、用户名称、用户密码、性别、出生日期、手机、地址、用户角色、创建者、创建时间、更新者、更新时间、用户头像、工作照
    账单:订单编号、订单编码、商品名称、商品描述、商品单位、商品数量、商品总额、是否支付、创建者、创建时间、更新者、更新时间、供应商ID
    供应商:供应商ID、供货商编码、供货商名称、供应商详细描述、供应商联系人、联系电话、地址、微信、创建者、创建时间、更新时间、更新者、营业执照、组织机构代码证
    地址:主键ID、联系人姓名、收货地址明细、邮编、联系人电话、创建者、创建日期、修改者、修改时间、用户ID
    角色:角色编号、角色编码、角色名称、创建者、创建时间、修改者、修改时间

    3.3 数据库逻辑结构设计将概念结构设计中的各个模型转化为DBMS支持的表结构,同时保持不会出现插入异常、删除异常和修改异常,表结构应该做到符合3NF。根据系统 E-R 图,需要设计4个数据表来存放信息。在本系统中,一共有五个实体,实体转化为数据库模型为如下所示:
    一对多联系转化为一个关系模式:

    用户—订单(用户编号,订单编号)
    供货商—订单(供货商编号,订单编号)
    用户—身份(用户编号,身份编号)
    用户—地址(用户编号)

    利用以上关系模式得到的所有数据表如下所示:
    用户表(smbms_user)

    数据项:主键ID、用户编码、用户名称、用户密码、性别、出生日期、手机、地址、用户角色、创建者、创建时间、更新者、更新时间、用户头像、工作照
    说明:用户ID是唯一的用户标识,使此表的主键。如表3.3.1所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    主键ID


    userCode
    varchar
    15
    Not null
    用户编码


    userName
    varchar
    15
    Not null
    用户名称


    userPassword
    varchar
    15
    Not null
    用户密码


    gender
    int
    10

    性别


    birthday
    date


    出生日期


    phone
    varchar
    15

    手机


    address
    varchar
    30

    地址


    userRole
    int
    10

    用户角色


    createdBy
    bigint
    20

    创建者


    creationDate
    datetime


    创建时间


    modifyBy
    bigint
    20

    更新者


    modifyDate
    datetime


    更新时间


    idPicPath
    varchar
    300

    用户头像


    workPicPath
    varchar
    300

    工作照



    供应商表(smbms_provider)

    数据项:供应商ID、供货商编码、供货商名称、供应商详细描述、供应商联系人、联系电话、地址、微信、创建者、创建时间、更新时间、更新者、营业执照、组织机构代码证
    说明:这张表标识的是超市管理信息系统中商品供应商的信息列表,供应商ID是该表的主键
    编号方法:商品供应商ID采用自动生成方式,如表3.3.2所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    Bigint
    20
    Not null
    供货商ID(主键)


    proCode
    Varchar
    20
    Not null
    供货商编码


    proName
    varchar
    20
    Not null
    供货商名称


    ProDesc
    varchar
    50

    供应商详细描述


    proContact
    varchar
    20
    Not null
    供货商联系人


    proPhone
    Varchar
    20
    Not null
    联系电话


    ProAddress
    Varchar
    50
    Not null
    供货商地址


    proFax
    varchar
    20

    微信


    CreateBy
    bigint
    20

    创建者


    CreatationDate
    datetime


    创建时间


    modifyDate
    datetime


    更新时间


    modifyBy
    bigint
    20

    更新者


    companyLicPicPath
    varchar
    300

    营业执照


    orgCodePicPath
    varchar
    300

    组织机构代码证



    订单表(smbms_bill)

    数据项:订单编号、订单编码、商品名称、商品描述、商品单位、商品数量、商品总额、是否支付、创建者、创建时间、更新者、更新时间、供应商ID
    说明:这张表标识的是超市管理信息系统订单信息列表,订单ID是该表的主键
    编号方法:订单ID采用自动生成方式,供应商ID与供应商表中供应商ID一一对应,如表3.3.3所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    订单ID(主键)


    billCode
    varchar
    20
    Not null
    订单编码


    ProductName
    Varchar
    20
    Not null
    商品名称


    ProductDescent
    Varchar
    50
    Not null
    商品描述


    ProductUnit
    Varchar
    10
    Not null
    商品单位


    ProductCount
    Decimal
    20,2
    Not null
    商品数量


    totalPrice
    Decimal
    20,2
    Not null
    商品总额


    isPayment
    int
    10
    Not null
    是否支付


    createdBy
    bigint
    20

    创建者


    creationDate
    Datetime


    创建时间


    modifyBy
    bigint
    20

    更新者


    modifyDate
    datetime


    更新时间


    providerID
    Int
    20

    供应商ID



    身份表(smbms_role)

    数据项:角色编号、角色编码、角色名称、创建者、创建时间、修改者、修改时间
    说明:这张表标识的是超市订单管理信息系统中用户身份列表,身份编号是该表的主键
    编号方法:用户身份编号与用户表中的员工身份编号一一对应,如表3.3.4所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    角色ID(主键)


    RoleCode
    varchar
    15
    Not null
    角色编码


    roleName
    Varchar
    15
    Not null
    角色名称


    createdBy
    bigint
    20

    创建者


    creationDate
    datetime


    创建时间


    modifyBy
    bigint
    20

    修改者


    modifyDate
    datetime


    修改时间



    地址表(smbms_address)

    数据项:主键ID、联系人姓名、收货地址明细、邮编、联系人电话、创建者、创建日期、修改者、修改时间、用户ID
    编号方法:用户ID与用户表中的用户ID一一对应,如表3.3.5所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    主键ID(主键)


    Contact
    varchar
    15
    Not null
    联系人姓名


    addressDesce
    Varchar
    50
    Not null
    收货地址明细


    postcode
    Varchar
    15

    邮编


    Tel
    Varchar
    20
    Not null
    联系人电话


    createdBy
    bigint
    20

    创建者


    creationDate
    Datetime


    创建时间


    modifyBy
    bigint
    20

    修改者


    modifyDate
    datetime


    修改时间


    userID
    Bigint
    20

    用户ID



    数据库连接利用了SSM框架的底层的MyBatis,建立了实体类与MySQL之间映射关系,从而实现数据持久化、封装数据库连接等操作。
    3.4 数据库物理结构设计3.4.1 选择关系模式的存取方式对数据库逻辑结构设计中建立的表结构,供应商表的供应商编号属性唯一决定每一个供应商元组,所以对供应商表建立以供应商编号为主关键字的索引。同理,对管理员关系模式、订单关系模式也采用类似的索引存取方法。
    3.4.2 数据表存储结构设计本系统的所有数据表均存放在物理磁盘中。用户表、供应商表和订单表的结构是相对稳定的,表中的已有记录是要长期保存的,在此基础上系统会相应用户的操作对数据表进行增、删、改、查等操作。

    3.5 数据库的建立3.5.1 数据库的建立创建数据库
    create database smbms;USE smbms;
    创建表smbms_address
    DROP TABLE IF EXISTS `smbms_address`;CREATE TABLE `smbms_address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contact` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系人姓名', `addressDesc` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '收货地址明细', `postCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '邮编', `tel` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系人电话', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', `userId` bigint(20) DEFAULT NULL COMMENT '用户ID', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_bill
    DROP TABLE IF EXISTS `smbms_bill`;CREATE TABLE `smbms_bill` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `billCode` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '账单编码', `productName` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '商品名称', `productDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品描述', `productUnit` varchar(10) COLLATE utf8_unicode_ci NOT NULL COMMENT '商品单位', `productCount` decimal(20,2) NOT NULL COMMENT '商品数量', `totalPrice` decimal(20,2) NOT NULL COMMENT '商品总额', `isPayment` int(10) NOT NULL COMMENT '是否支付(1:未支付 2:已支付)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `providerId` int(20) DEFAULT NULL COMMENT '供应商ID', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_provider
    DROP TABLE IF EXISTS `smbms_provider`;CREATE TABLE `smbms_provider` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `proCode` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商编码', `proName` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商名称', `proDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商详细描述', `proContact` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商联系人', `proPhone` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系电话', `proAddress` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '地址', `proFax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '微信', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `companyLicPicPath` varchar(300) DEFAULT NULL COMMENT '营业执照', `orgCodePicPath` varchar(300) DEFAULT NULL COMMENT '组织机构代码证', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_role
    DROP TABLE IF EXISTS `smbms_role`;CREATE TABLE `smbms_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `roleCode` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '角色编码', `roleName` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '角色名称', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_user
    DROP TABLE IF EXISTS `smbms_user`;CREATE TABLE `smbms_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `userCode` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户编码', `userName` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户名称', `userPassword` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户密码', `gender` int(10) DEFAULT 2 COMMENT '性别(1:女、 2:男)', `birthday` date DEFAULT NULL COMMENT '出生日期', `phone` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '手机', `address` varchar(30) COLLATE utf8_unicode_ci NOT NULL COMMENT '地址', `userRole` int(10) DEFAULT NULL COMMENT '用户角色(取自角色表-角色id)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `idPicPath` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户头像', `workPicPath` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '工作照', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    3.5.2 初始数据的输入数据表创建成功后,数据库中还没有实际的数据。为了保证外部键能使用,数据需要提前输入,如用户编码、用户姓名、订单名称和供应商等等。具体插入语句如下:
    向smbms_address表插入数据
    insert into `smbms_address`(`id`,`contact`,`addressDesc`,`postCode`,`tel`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`userId`) values (1,'王丽','北京市东城区东交民巷44号','100010','13678789999',1,'2020-04-13 00:00:00',NULL,NULL,1),(2,'张红丽','北京市海淀区丹棱街3号','100000','18567672312',1,'2020-04-13 00:00:00',NULL,NULL,1),(3,'任志强','北京市东城区美术馆后街23号','100021','13387906742',1,'2020-04-13 00:00:00',NULL,NULL,1),(4,'曹颖','北京市朝阳区朝阳门南大街14号','100053','13568902323',1,'2020-04-13 00:00:00',NULL,NULL,2),(5,'李慧','北京市西城区三里河路南三巷3号','100032','18032356666',1,'2020-04-13 00:00:00',NULL,NULL,3),(6,'王国强','北京市顺义区高丽营镇金马工业区18号','100061','13787882222',1,'2020-04-13 00:00:00',NULL,NULL,3);
    向smbms_bill表插入数据
    insert into `smbms_bill`(`id`,`billCode`,`productName`,`productDesc`,`productUnit`,`productCount`,`totalPrice`,`isPayment`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`providerId`) values (1,'BILL2016_001','洗发水、护发素','日用品-洗发、护发','瓶','500.00','25000.00',2,1,'2020-06-14 13:02:03',NULL,NULL,13),(2,'BILL2016_002','香皂、肥皂、药皂','日用品-皂类','块','1000.00','10000.00',2,1,'2020-03-23 04:20:40',NULL,NULL,13),(3,'BILL2016_003','大豆油','食品-食用油','斤','300.00','5890.00',2,1,'2020-05-14 13:02:03',NULL,NULL,6),(4,'BILL2016_004','橄榄油','食品-进口食用油','斤','200.00','9800.00',2,1,'2020-04-10 03:12:13',NULL,NULL,7),(5,'BILL2016_005','洗洁精','日用品-厨房清洁','瓶','500.00','7000.00',2,1,'2020-05-14 13:02:03',NULL,NULL,9),(6,'BILL2016_006','美国大杏仁','食品-坚果','袋','300.00','5000.00',2,1,'2020-04-14 06:08:09',NULL,NULL,4),(7,'BILL2016_007','沐浴液、精油','日用品-沐浴类','瓶','500.00','23000.00',1,1,'2020-07-01 10:10:22',NULL,NULL,14),(8,'BILL2016_008','不锈钢盘碗','日用品-厨房用具','个','600.00','6000.00',2,1,'2020-04-14 05:12:13',NULL,NULL,14),(9,'BILL2016_009','塑料杯','日用品-杯子','个','350.00','1750.00',2,1,'2020-02-04 11:40:20',NULL,NULL,14),(10,'BILL2016_010','豆瓣酱','食品-调料','瓶','200.00','2000.00',2,1,'2020-01-29 05:07:03',NULL,NULL,8),(11,'BILL2016_011','海之蓝','饮料-国酒','瓶','50.00','10000.00',1,1,'2020-04-14 16:16:00',NULL,NULL,1),(12,'BILL2016_012','芝华士','饮料-洋酒','瓶','20.00','6000.00',1,1,'2020-06-09 17:00:00',NULL,NULL,1),(13,'BILL2016_013','长城红葡萄酒','饮料-红酒','瓶','60.00','800.00',2,1,'2020-04-14 15:23:00',NULL,NULL,1),(14,'BILL2016_014','泰国香米','食品-大米','斤','400.00','5000.00',2,1,'2020-05-09 15:20:00',NULL,NULL,3),(15,'BILL2016_015','东北大米','食品-大米','斤','600.00','4000.00',2,1,'2020-05-14 14:00:00',NULL,NULL,3),(16,'BILL2016_016','可口可乐','饮料','瓶','2000.00','6000.00',2,1,'2020-03-27 13:03:01',NULL,NULL,2),(17,'BILL2016_017','脉动','饮料','瓶','1500.00','4500.00',2,1,'2020-05-10 12:00:00',NULL,NULL,2),(18,'BILL2016_018','哇哈哈','饮料','瓶','2000.00','4000.00',2,1,'2020-06-24 15:12:03',NULL,NULL,2);
    向smbms_provider表插入数据
    insert into `smbms_provider`(`id`,`proCode`,`proName`,`proDesc`,`proContact`,`proPhone`,`proAddress`,`proFax`,`createdBy`,`creationDate`,`modifyDate`,`modifyBy`) values(1,'BJ_GYS001','北京三木堂商贸有限公司','长期合作伙伴,主营产品:茅台、五粮液、郎酒、酒鬼酒、泸州老窖、赖茅酒、法国红酒等','张国强','13566667777','北京市丰台区育芳园北路','010-58858787',1,'2020-03-21 16:52:07',NULL,NULL),(2,'HB_GYS001','石家庄帅益食品贸易有限公司','长期合作伙伴,主营产品:饮料、水饮料、植物蛋白饮料、休闲食品、果汁饮料、功能饮料等','王军','13309094212','河北省石家庄新华区','0311-67738876',1,'2020-04-13 04:20:40',NULL,NULL),(3,'GZ_GYS001','深圳市泰香米业有限公司','初次合作伙伴,主营产品:良记金轮米,龙轮香米等','郑程瀚','13402013312','广东省深圳市福田区深南大道6006华丰大厦','0755-67776212',1,'2020-03-21 16:56:07',NULL,NULL),(4,'GZ_GYS002','深圳市喜来客商贸有限公司','长期合作伙伴,主营产品:坚果炒货.果脯蜜饯.天然花茶.营养豆豆.特色美食.进口食品.海味零食.肉脯肉','林妮','18599897645','广东省深圳市福龙工业区B2栋3楼西','0755-67772341',1,'2020-03-22 16:52:07',NULL,NULL),(5,'JS_GYS001','兴化佳美调味品厂','长期合作伙伴,主营产品:天然香辛料、鸡精、复合调味料','徐国洋','13754444221','江苏省兴化市林湖工业区','0523-21299098',1,'2020-02-22 16:52:07',NULL,NULL),(6,'BJ_GYS002','北京纳福尔食用油有限公司','长期合作伙伴,主营产品:山茶油、大豆油、花生油、橄榄油等','马莺','13422235678','北京市朝阳区珠江帝景1号楼','010-588634233',1,'2020-03-21 17:52:07',NULL,NULL),(7,'BJ_GYS003','北京国粮食用油有限公司','初次合作伙伴,主营产品:花生油、大豆油、小磨油等','王驰','13344441135','北京大兴青云店开发区','010-588134111',1,'2020-04-13 00:00:00',NULL,NULL),(8,'ZJ_GYS001','慈溪市广和绿色食品厂','长期合作伙伴,主营产品:豆瓣酱、黄豆酱、甜面酱,辣椒,大蒜等农产品','薛圣丹','18099953223','浙江省宁波市慈溪周巷小安村','0574-34449090',1,'2020-01-21 06:02:07',NULL,NULL),(9,'GX_GYS001','优百商贸有限公司','长期合作伙伴,主营产品:日化产品','李立国','13323566543','广西南宁市秀厢大道42-1号','0771-98861134',1,'2020-03-21 19:52:07',NULL,NULL),(10,'JS_GYS002','南京火头军信息技术有限公司','长期合作伙伴,主营产品:不锈钢厨具等','陈女士','13098992113','江苏省南京市浦口区浦口大道1号新城总部大厦A座903室','025-86223345',1,'2020-03-25 16:52:07',NULL,NULL),(11,'GZ_GYS003','广州市白云区美星五金制品厂','长期合作伙伴,主营产品:海绵床垫、坐垫、靠垫、海绵枕头、头枕等','梁天','13562276775','广州市白云区钟落潭镇福龙路20号','020-85542231',1,'2020-01-21 06:12:17',NULL,NULL),(12,'BJ_GYS004','北京隆盛日化科技','长期合作伙伴,主营产品:日化环保清洗剂,家居洗涤专卖、洗涤用品网、墙体除霉剂、墙面霉菌清除剂等','孙欣','13689865678','北京市大兴区旧宫','010-35576786',1,'2020-01-21 12:51:11',NULL,NULL),(13,'SD_GYS001','山东豪克华光联合发展有限公司','长期合作伙伴,主营产品:洗衣皂、洗衣粉、洗衣液、洗洁精、消杀类、香皂等','吴洪转','13245468787','山东济阳济北工业区仁和街21号','0531-53362445',1,'2020-01-28 10:52:07',NULL,NULL),(14,'JS_GYS003','无锡喜源坤商行','长期合作伙伴,主营产品:日化品批销','周一清','18567674532','江苏无锡盛岸西路','0510-32274422',1,'2020-04-23 11:11:11',NULL,NULL),(15,'ZJ_GYS002','乐摆日用品厂','长期合作伙伴,主营产品:各种中、高档塑料杯,塑料乐扣水杯(密封杯)、保鲜杯(保鲜盒)、广告杯、礼品杯','王世杰','13212331567','浙江省金华市义乌市义东路','0579-34452321',1,'2020-06-22 10:01:30',NULL,NULL);
    向smbms_role表插入数据
    insert into `smbms_role`(`id`,`roleCode`,`roleName`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'SMBMS_ADMIN','系统管理员',1,'2020-01-01 00:00:00',NULL,NULL),(2,'SMBMS_MANAGER','经理',1,'2020-02-02 00:01:00',NULL,NULL),(3,'SMBMS_EMPLOYEE','普通员工',1,'2020-02-03 00:00:00',NULL,NULL);
    向smbms_user表插入数据
    insert into `smbms_user`(`id`,`userCode`,`userName`,`userPassword`,`gender`,`birthday`,`phone`,`address`,`userRole`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'admin','系统管理员','1234567',1,'1983-10-10','13688889999','山东省日照市东港区成府路207号',1,1,'2020-03-21 16:52:07',NULL,NULL),(2,'liming','李明','0000000',2,'1983-12-10','13688884457','山东省日照市东港区前门东大街9号',2,1,'2020-03-01 00:00:00',NULL,NULL),(5,'hanlubiao','韩路彪','0000000',2,'2001-06-05','18567542321','山东省日照市东港区北辰中心12号',2,1,'2020-02-11 19:52:09',NULL,NULL),(6,'zhanghua','张华','0000000',1,'1980-06-15','13544561111','山东省日照市东港区学院路61号',3,1,'2020-02-11 10:51:17',NULL,NULL),(7,'wangyang','王洋','0000000',2,'2001-12-31','13444561124','山东省青岛市三二二区西二旗辉煌国际16层',3,1,'2020-06-11 19:09:07',NULL,NULL),(8,'zhaoyan','赵燕','0000000',1,'1999-03-07','18098764545','山东省青岛市东科区回龙观小区10号楼',3,1,'2020-04-21 13:54:07',NULL,NULL),(10,'sunlei','孙磊','0000000',2,'1998-01-04','13387676765','山东省日照市朝阳区管庄新月小区12楼',3,1,'2020-05-06 10:52:07',NULL,NULL),(11,'sunxing','孙兴','0000000',2,'1997-03-12','13367890900','北京市朝阳区建国门南大街10号',3,1,'2020-01-09 16:51:17',NULL,NULL),(12,'zhangchen','张晨','0000000',1,'1986-03-28','18098765434','朝阳区管庄路口北柏林爱乐三期13号楼',3,1,'2019-06-09 05:52:37',1,'2020-04-14 14:15:36'),(13,'dengchao','邓超','0000000',2,'1981-11-04','13689674534','北京市海淀区北航家属院10号楼',3,1,'2020-07-01 08:02:47',NULL,NULL),(14,'yangguo','杨过','0000000',2,'1989-01-01','13388886623','北京市朝阳区北苑家园茉莉园20号楼',3,1,'2020-02-01 03:52:07',NULL,NULL),(15,'zhaomin','赵敏','0000000',1,'1989-12-04','18099897657','山东省临沂市昌平区天通苑3区12号楼',2,1,'2020-01-12 12:02:12',NULL,NULL);
    此外,本系统中所用到的用户性别和用户身份代码如表3.5.1至表3.5.2所示。
    用户性别代码表



    代码
    说明




    1



    2




    用户身份代码



    代码
    说明




    1
    系统管理员


    2
    经理


    3
    普通员工



    4 各功能模块的设计与实现4.1 系统开发条件4.1.1 开发语言系统使用的开发语言是Java。Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程等特点。Java可以编写桌面应用程序、Web应用程序、分布式系统应用程序等。正是因为Java语言拥有如此诸多的优秀特性,所以我们选择了它作为开发超市订单管理系统,使得整个开发、调试过程更加高效。
    4.1.2 开发框架超市订单管理系统以SSM架构作为支撑,分为表现层、业务层和持久层三层,实现后台数据更新。该架构由Spring MVC、Spring和MyBatis三个开源框架整合而成,用于开发结构合理,性能优越,代码健壮的应用程序。

    4.1.3 前端框架由于本系统是Web应用,所以使用了HTML5+CSS3+JavaScript的方式实现前端页面。实现过程中参考了Bootstrap前端开发框架。Bootstrap是Twitter退出的一个用于前端开发的开源工具包。在设计前端页面时,参考了Bootstrap的相关开源代码。
    4.1.4 集成开发环境编程所使用的集成开发环境是Eclipse,是著名的跨平台的自由集成开发环境(IDE)。Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。本次系统便选用了Eclipse作为开发平台。
    4.1.5 Web应用服务器Tomcat由Apache、Sun和其他一些公司及个人共同开发而成。由于有了Sun的参与和支持,最新的Servlet和JSP规范可以在Tomcat中得到体现。因为Tomcat技术先进、性能稳定,因而成为目前比较流行的Web应用服务器。本次系统选用的便是Tomcat作为应用服务器。
    4.1.6 数据库管理系统本系统使用的数据库管理系统是MySQL Community。MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发。在WEB应用方面,MySQL是最好的RDBMS (Relational Database Management System,关系数据库管理系统)应用软件。
    系统中的数据库以及数据库中的所有关系模式都使用MySQL进行处理。
    4.2 用户界面设计完成数据库创建和功能说明以后,我们进行下一步工作,即设计用户界面,完成了系统要求的 5 项主要功能。
    我们把超市订单管理系统的窗体分成5个主要部分,如下图所示。
    4.2.1 订单管理
    4.2.2 供应商管理
    4.2.3 用户管理
    4.2.4 修改密码
    4.2.5 登录注销
    4.3 功能模块说明5.3.1 订单信息添加、查询、修改与删除订单信息查看:为了对订单浏览信息,能够实现浏览的功能是十分必要的。管理员输入需要搜索的相应信息,点击查看按钮后系统将寻找到的数据展示到网页中。

    订单信息添加:作为超市订单管理系统,订单信息的管理是很重要的。每当采购部门增加新的订单时,订单信息就要增加。超市也可能因为其它原因增加订单信息,订单添加模块都可以做出快捷的解决方案。管理员输入相应的信息,点击提交后系统将数据保存到数据库中。

    订单信息修改:根据订单编号可以查询订单详细信息,然后修改订单的所有信息。系统从数据库中读取数据并显示到页面上,管理员修改数据后,点击修改按钮,系统将更新表中的数据。

    订单信息删除:根据订单编号可以删除该订单的信息。管理员选择需要删除订单名称并点击删除按钮,系统将从数据库中删除相应数据。
    订单信息查询:在成千上万种商品种,如果人为寻找某一个商品肯定是不可能的,只有通过商品信息查询模块才能为用户或管理人员解决这个难题。根据订单名称可以查询该订单的信息。管理员输入订单名称并点击查询按钮,系统将从数据库中查询相应的数据并显示到页面上。

    5.3.2 供应商信息添加、查询、修改与删除供应商查询界面:供应商查询界面提供了供应商的信息,可根据供应商名称的关键字进行筛选查询,并提供了添加供应商、查看供应商详细信息、修改供应商信息、删除供应商的功能。

    供应商查看详情界面:在供应商查询界面点击具体供应商操作列表的查看按钮,可以查看供应商的具体信息,包括:供货商编码、供货商名称、详细描述、联系人、联系电话、地址、微信。

    供应商修改页面:若供应商信息变动,管理员可通过供应商信息修改功能对供应商信息进行更新,更新后的数据将保存到数据库中。

    商品供应商信息删除:企业倒闭或者经营策略的改变,当它对超市商品的供应没有作用时,商品供应商厂家信息的删除是正常的。管理员输入供应商名称查询数据表中的数据并显示到页面上,点击删除后系统将表中的相应数据删除。
    供应商添加界面:与供应商达成交易后,管理员在供应商添加页面填写供应商具体信息,填写完毕点击提交,添加后的数据将保存到数据库中。

    5.3.3 用户信息添加、查询、修改与删除用户管理页面:通过输入用户名和身份查询用户。当不记得用户名的具体名字时,只输入用户名的其中一个字,会检索出所有带这个字的用户,方便管理员查询管理。点击右边链接添加用户,会连接到相关网页添加用户信息。点击操作里的查看、修改等可以进行相应的改、删、查操作。

    用户信息删除:当企业员工离职时,或者经过一段时间后,会发现用户表中一些信息时无用的,用户删除模块可以解决这样的问题。
    添加用户信息:填写用户相关信息,下面有两个按钮,可以选择重置或者提交。

    5.3.4 修改密码为了系统的安全,用户的应该只有用户个人才能修改,这不仅保证了整个公司的利益也保护了个人隐私。用户在输入相应的用户编号,填写旧密码以及新密码后,点击提交,重置密码成功。发现输入错误时,可以手动删除或者点击重置按钮,重新填写。

    修改用户密码成功后,会弹出修改用户密码成功页面,如图4.3.14所示。

    5.3.5 登录/注销输入用户名以及用户密码登录进入超市订单管理界面,可以查看管理信息。管理员可以对相关数据进行增、改、查等操作,也可以注销退出系统。
    5 实训总结5.1 所遇困难在实现本系统时遇到的困难主要体现在两个方面,一是系统的前端页面的设计,二是怎样Web与数据库实现交互。
    系统前端页面的设计困难的解决是通过参考著名的前端框架Bootstrap实现的。Bootstrap框架提供了许多精美的组建、布局,还开放了源代码供参考。在此基础上我们还加入了一些利用JavaScript代码实现的美化效果,使得前端设计更加美观。
    实体Web与数据库交互的解决得益于SSM框架的三层Spring MVC、Spring和MyBatis,能够分离处理数据库与Web层的视图,从而达到交互的目的。
    此外,在编写后端的时候,变量的大小写、系统配置也是困难重重。好在,在反复编写之后,迅速熟悉的技巧,能够让页面自由切换。系统配置更是反复在网上求证,得以解决。
    5.2 实验心得这一次合作开发超市订单管理系统,从开始选择课题的困惑到最终完成了一个我们还算满意的作品,使我学到了很多东西。从设计数据库到编写后台代码,链接数据库,在网页上显示,令人印象深刻。反复查阅资料,启动Tomcat到凌晨0点,都是藏着对这次项目的努力。其实,从一开始选择哪个题目是否用SSM框架来开发我一直也犹豫过,像国内势头正旺的ThinkPHP,易学易用,完善的中文开发文档,遇到问题或者bug可以非常容易的在中文社区得到解答。但是我最后选择了SSM框架,不仅仅因为它广泛,而是我希望能够挑战自己。经过这一个周的磨练,我最大的收获除了学到了真正可以应用的知识外,更重要的是学会了项目合作开发的经验。
    20 留言 2020-08-05 15:32:05 奖励46点积分
  • 基于SSM框架的B/S微博系统的设计与实现

    第一章 绪 论时至今日,网络对于现代人来说,早已成为人类科技发展进步的桥梁,而通过网络衍生出的一系列产品也不断的冲击着人们的日常生活,截至2016年年底,我国网名数量达到7亿,有超多一半多人在使用网络,而它天涯咫尺的作用,不但消除了人与人地域上的距离,更是拉近了心灵的距离,沟通与互动变的异常频繁与重要。
    随着互联网新时代的来临,微博借着互联网的桥梁,逐渐进入网名的视野中,没有博客的长篇大论,也不需要严谨的逻辑层次,这使得网络中一大批的原创文章被生产发掘,短短几句话便可以在网络中激起千层浪,普通人也可能在一夜之间成为拥有数千万粉丝的“网红”。微博的便捷、原创、和草根性使它成为中国网民茶余饭后的网上生活。
    早在2006年3月,Obvious推出了Twitter服务,这个世界上最早同时也是最著名的微博系统,而在那是,微博也仅仅只是为了给好友发送手机短信,在中国,饭否网的上线标志中国微博的开端,之后腾讯滔滔、叽歪、嘀咕等微博的开荒者终究也都没能站在最后。
    2010年,我国的微博得到迅速发展,这一年,无论微博的用户还是影响力都达到前所未有的高度,以新浪门户为首的腾讯、新浪、搜狐等微博展现出全新的活力。到了2013年6月,中国微博用户规模高达3.31亿人,在微博中流动的信息有超过2亿多条。
    2010年11月,新浪微博推出群组功能,这个功能不但可以与好友实时联系,又可以随时发布最新信息,2012年添加新功能“悄悄关注”,在用户关注后不通知被关注用户,也不显示给被关注用户,2013年,微博推出包括iPhone和Android的移动客户端,新增“密友”功能,同年10月份新浪微博“粉丝服务平台”上线,粉丝服务平台帮助认证用户为订阅自己的用户提供精彩内容和互动服务,自此微博由“营销”向“营销+服务”转型!而新浪微博依旧延续这自己的名人效应,一个普通账号,在没有粉丝关注的情况下,发布的微博却很难被他人看到,如果需要在微博中求助,没有“名人大V”的帮助,很难被别人所注意到。想要在微博中寻求帮助,就需要微博提供更多的服务。而本系统通过使用积分悬赏功能使得用户可以通过积分悬赏自己的问题,来让更多的用户回答自己的问题,得到更多人的帮助。
    第二章 相关技术介绍2.1 架构概述B/S架构(Browser/Server,浏览器/服务器模式):是一种通过将浏览器作为客户端的网络结构模式,利用已经逐步成熟的web浏览器技术,结合浏览器的多种功能,使用浏览器来作为早先C/S(Client/Serve)架构下复杂的客户端,使用C/S架构使得用户的客户端得到统一,将软件系统的核心功能集中在服务器端,系统的升级和维护更加简单,开发人员只需要管理服务器就可以做到对如软件系统的更新和维护,B/S架构所带来的众多优点使得它成为将来信息化发展的主流方向。
    MVC模式:即模型(Model),视图(View),控制器(Controller)是一种软件开发的设计模式,Model主要负责分离出来的业务逻辑的数据交互,View主要负责数据的提交和展示,Controller主要负责获取请求和请求的转发。
    SSM框架(SpringMVC+Spring+Mybatis):由这三个开源框架组成的web项目的框架,是标准的MVC模式,spring MVC负责请求的转发和视图管理,spring实现业务对象管理和事务控制,mybatis作为持久化引擎,相对于早先流行的SSH(Struts+Spring+Hibernate)框架来说,SSM作为轻量级框架更适合开发中小型项目。
    2.2 关键技术简介前端技术
    JSP(Java Server Pages):嵌入了java代码的html页面,本质是一个servlet,它实现了在htmld的语法中扩展java代码,使用<% %>格式。
    JavaScript:是一种弱类型的脚本语言,由ECMAScript描述JavaScript的基本对象和语法,文档对象模型(DOM),浏览器对象模型(BOM)三部分组成。
    Ajax(Asynchronous Javascript And XML):异步的JavaScript和XML,实现前后台之间的异步交互,在不需要重新加载整个页面的前提下对页面上的部分内容做局部刷新。

    后台技术java:一种面向对象的编程语言,主要特性包括有

    简单性:抛弃了C++中复杂的语法和指针多继承等特征,开发人员不需要关注底层优化,只需要关注业务逻辑就行
    面向对象性,对程序员而言,只需要注意对应的数据和处理数据的方法,不用严格按照过程的方式来编写代码,因为java语言是面对对象的,因此也拥有面向对象的三大特征,继承、封装和多态
    跨平台性,java语言的跨平台性也就是JVM(java虚拟机)的跨平台性。Java文件编译后不会直接生成机器直接运行的二进制文件,而是编译成class文件,JVM会将编译的class文件根据不同操作系统下的JVM来生成对应系统的二进制文件,只需要编译一次,就可以在不同的平台上运行

    SpringMVC:是Spring框架提供的一个模块,通过实现MVC模式来地将数据、业务与展示进行分离,简化了Web开发。和其他MVC框架一样都是请求驱动,控制转发,它的核心Servlet是DispatcherServlet,即前端控制器,用来分发请求到控制器,它和Spring框架完全整合。这也使得SpringMVC成为目前非常流行的MVC框架。
    Spring:一款轻量级java开发框架,Spring框架有着分层的体系结构,因此可以使用Spring中 独立的任意部分。而它的架构依旧是内在稳定的,Spring提供了J2EE应用各层解决方案,贯穿于三层架构的每一层,但Spring依旧可以很好的兼容其他框架,本项目主要用到了Spring中IOC(控制反转)和AOP(面向切面编程)模块。
    Mybatis:是一个简化Java持久化层的轻量级开源框架。并且支持注解的Mapper,Mybatis消除了绝大部分的JDBC代码,使得java对象持久化到数据库的过程变的更加容易,相对于其他的java持久化框架,Mybatis的优点在于,消除了大量的JDBC冗余代码、简单易学、可见的Sql语句、提供了于Spring的整合,引入更好的性能。
    2.3 开发工具Tomcat服务器:是一个Web应用服务器,它是轻量且开源的,是中小型Web项目和开发调试和学习的首选。
    Oracle数据库(Oracle Database):是由Oracle公司开发的一款关系型数据库,是商业运用第一的关系型数据库,系统使用方便,功能强大,可移植性强。适用于各种大中小环境,在大数据时代,作为分布式数据库,它实现了分布式处理的功能,具有完整的数据管理功能、完备的关系型数据库、分布式处理功能。
    Eclipse开发工具:一个基于java开源的可扩展开发平台,它不但包括java集成开发环境,还包括插件开发环境,如SVN、CVS等团队开发插件。
    2.4 本章小结本章主要介绍了开发项目用到的一些主要技术,项目所使用的架构和设计模式,项目中所使用到的主要框架技术,项目在浏览器端展示用到的前端技术和展示方式,后台代码使用的开发语言,使用的服务器技术,数据持久层所使用的数据库等,在本章最后又介绍了开发使用到的开发工具。
    第三章 系统需求分析3.1 可行性分析3.1.1技术可行性在已有技术方面,为了统一客户端,消除因版本升级和维护带来的复杂性,因此采用成熟的B/S架构在项目的实现上完全可行,在开发语言和框架方面,java和j2ee体系的强大,可以让开发人员精心的构建web项目,以及一系列的开源框架,都为项目的可行性提供了强大的依据,在服务器方面,使用开源服务器Tomcat,足以支持该小型项目的正常使用,而不断发展的前端技术和前端框架可以制作精美的前台页面,提高用户的体验和交互,这在项目的页面展示技术上完全可行,强大的关系型数据库为项目数据的持久化提供强有力的后援。综上所述,日趋成熟化的java和j2ee体系、完全开源的java框架和服务器、功能强大的关系型数据库、运用Web前端技术提供用户交互页面,所以该项目在技术方面完全可行。
    3.1.2 经济可行性在互联网发展到信息时代的今天,单一获取信息的方式已经不能满足人们,人们每天接触的信息越来越多,而获取信息的形式也越来越多,但大多数获取信息的方式,留给用户交互的方式并不多,大都数情况下,人们只能被动获取,而很难找到自己的喜好和需求来获取信息,而本微博项目可以让人们获取实时热点信息和他们所关注的信息,实时与微博信息交互。而使用大量成熟技术与开源框架下,也使得小项目的开发更加简单,经济,高效,因微博而兴起的微博效应也能带来一定的经济效益。
    3.1.3 操作可行性微博系统使用B/S架构,用户不需要下载客户端,只需要用户有浏览器,就可以在浏览器上登陆微博系统,微博系统的界面颜值高,用户交互性高,用户操作简单方便,只需要了解基本的计算机操作就能使用,用户体验性高。因此在系统操作上完全可行。
    3.2 需求分析3.2.1 系统总体需求该微博系统主要由前台用户模块和后台管理模块组成,当用户进入首页时可以选择登陆或不登陆,登陆时可以使用已有账号登陆或注册新账号。用户未登陆时,在首页显示最近更新的热门微博,而登陆后的用户可以在首页看到 自己关注用户最近更新的微博。
    未登陆用户只能搜索查看微博信息和访问用户主页,登陆用户可以登陆系统后修改自己的基本信息例如签名、性别等,在验证用户信息后还可以修改密码和密保信息。以及修改用户头像和密码,编辑自己的个人主页,对微博进行点赞、评论、收藏等功能,还可以关注/取消关注用户,拉黑用户、私聊用户等操作。
    后台管理员可以查看系统所有的数据,包括用户、微博、评论、海螺、回答的总量,最近一个的数据库,最近一周的数据量。具体所具有的功能包括管理不良微博信息与不良账号,对微博、微博评论、海螺、回答等信息的删除和恢复功能,对不良账号的封禁等操作。
    微博查找模块:用户可以输入关键字来查找相对应的微博或查找用户。
    微博发布模块:用户点击发布,在内容中添加自己想要发送的内容,可以选择表情,也可以插入图片,但对输入字符数有着限制,同时还可以插入一张图片。
    微博评论模块:用户可以查看微博的评论,发布微博评论等。
    神奇海螺模块:用户可以发布一个神奇海螺,海螺主要用来记载用户提出的各种问题,由其他用户来查看并回答问题,当回复者的答案被提问者采纳后,回答者可以增加自己的海螺积分,不同的海螺积分有着不同的海螺称号。
    积分模块:用户每天登陆,发微博,做任务等方式可以增加自己的积分,不同的积分拥有不同的称号,神奇海螺模块的积分有着不同的称号,称号显示在用户名称的后面。
    好友模块:用户可以查看系统中其他的用户,找到自己喜爱的用户然后关注他们,关注后可以在好友模块中查看自己关注的用户,以及好友最近发布的微博等信息,也可以私信好友,发送私信信息给好友。
    3.2.2 用例图需求1. 当用户进入系统时候,可以选择登陆或注册用户,如果忘记了密码还可以通过密保问题来重置密码。

    2. 当用户登陆后,可以管理用户的个人基本信息,修改用户基础信息,修改用户密保信息。修改用户头像等功能。

    3. 用户微博管理系统,当用户登陆进去系统时,可以在首页发布微博,通过关键字搜索微博内容中关键字的微博信息。产看微博,包括查看个人微博、好友微博、推荐微博。

    4. 评论管理,评论管理依赖于微博模块,用户可以查看微博的评论,对微博信息发布评论,以及删除自己的评论。

    5. 海螺管理,用户登陆后可以在海螺模块发布海螺问题,发布问题时可以选择悬赏的积分数目,同时减少自己的积分数,用户可以参加回答他人的海螺问题,当回答的答案被采纳时,就可以获得用户悬赏的积分数。

    6. 好友管理,当用户登陆时可以关注系统推荐好友,也可以自己搜索用户,查看用户的主页面,添加关注或取消关注用户,还可以给用户发送私人信息,或者拉黑用户。

    7. 消息通知,当用户的关注,微博评论,点赞,收藏时调用消息通知。

    8. 管理员:管理员登陆系统,可以管理微博用户,对不良用户进行封禁和注销账户的操作,也可以恢复用户的状态,同时也可以对微博信息进行管理,如删除微博,恢复被管理员删除的微博信息等,对微博评论的删除和恢复等操作。

    第四章 系统功能设计4.1 系统类分析4.1.1 实体类实体类主要用来传递数据,主要包括User、Friend、PointAction、PointRecord、Weibo、WeiboCollect、WeiboComm、WeiboLike、WeiboTrans等,用户类中包括用户的基本信息,微博类中包括微博的基本信息同时包含实体用户类,好友类包括好友编号,好友创建时间与关注双方的用户类等,私信类包含私信双方的用户类与私信的基本属性,微博收藏类包含收藏的微博类与用户id等收藏属性,微博评论类包含微博类和用户类以及评论内容等属性,微博转发类包含用户类和微博类以及转发时间等属性,微博点赞类包含微博类和用户类以及点赞时间等属性。实体类之间的类关联关系如图所示。

    4.1.2 控制器类在controller层包含MainController、WeiboController、FrendController、AdminController四个JAVA类,在SpringMVC框架中主要用来接收浏览发送给服务器的请求和数据处理并控制请求的转发,将从Service层中获取的数据响应给浏览器端。MainController主要用发来接收来自用户相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    WeiboController主要用发来接收来自微博相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图4所示。

    FriendController主要用发来接收来自好友相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    AdminController主要用发来接收来自管理员相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    4.1.3 业务逻辑类在Service层中主要包含四个Service接口和他们的实现类,包括IUserService用来处理用户业务例如用户注册、登陆、修改个人信息等,如图4.6所示。IWeiboService用来处理微博相关的业务例如查找微博,发布微博,删除微博以及对微博的相关操作例如点赞、评论等功能的业务实现,如图4.7所示。IFriendService用来处理和用户好友相关的业务例如点赞、取消点赞、私信、拉黑等功能的业务实现,如图4.8所示。IAdmoinService用来处理和管理员相关的业务例如管理员登陆、图表展示、用户管理、微博管理、海螺管理功能的业务实现,如图4.8所示。

    图4.6用户业务处理类图

    图4.7微博业务处理类图

    图4.8微博控制器类图
    4.1.4 数据库交互类由于系统采用了MyBatis持久化框架,开发人员不需要关注和数据库之间具体的JDBC代码,而只需要处理业务逻辑,因此只需要在Dao层接口中声明方法而不需要写接口的实现类来实现方法,则是通过配置对应的配置文件,在配置文件中编写对应接口方法中的SQL语句和数据库交互。
    4.2 关键业务设计4.2.1 登陆系统
    图4.9系统登录顺序图
    用户进入主页后,可以在左边选择注册用户,或者在右边登陆系统,在注册模块,用户输入用户邮箱,密码,昵称等信息,当用户输入邮箱后会通过Ajax将用户输入的邮箱传到后台,查找用户邮箱是否已经被注册,如果被注册则在页面提示用户该邮箱已被注册,在输入基本信息点击注册后,保存用户注册信息。
    登陆功能:当用户输入邮箱地址和密码后,如果点击下次自动登陆密码在点击登陆后,判断用户的当前登陆时间和上次登陆时间是否为同一天,如果不是同一天则为用户增加登陆的 积分,然后跳转至微博首页。
    4.2.1 用户信息系统
    图4.10用户修改信息顺序图
    用户基本信息:在系统的首页中点击个人账号设置后,跳转到修改用户基本信息页面,用户可以修改这些基本信息,并将修改后的信息保存在数据库中。
    修改密码:在用户个人资料页面点击修改密码,跳转修改密码页面,用户可以输入用户的当前密码,系统判断密码是否正确,如果密码不正确,显示当面密码错误,如果输入密码正确,用户则可以输入新密码,点击修改后将修改后的密码更新到数据库中。
    修改用户头像:点击用户个人资料中修改头像,跳转至修改头像页面,用户选择头像文件,点击上传,将用户头像保存在服务器上,判断用户之前头像是为系统默认头像,如果不是就删除用户之前的头像图片,点击修改后将修改后的头像地址保存在数据库中。
    修改密保:用户先要根据之前设置的密保问题来填写答案,如果密保答案错误,提示用户密保答案错误,如果密保答案正确,用户可以输入新的密保问题和密保答案,点击修改保存用户新的密保问题和答案。
    4.2.2微博模块
    图4.11系统登录顺序图
    发布微博:用户可以在首页发布微博,在微博信息中可以插入表情,也可以选择插入一张图片,当用户点击发布后,浏览器发送请求将前台页面表单中微博信息和图片信息传入后台控制器,将图片信息保存在服务器中,在数据库中只保存图片路径,最后将微博信息保存在数据库中。
    搜索微博:用户可以在首页的搜索框中输入微博中提到的内容来搜索,系统在数据库中通过迷糊查询查询相关的微博信息。在页面中将搜索到的页面展示出来,并且将关键字标红显示。
    微博操作:用户可以查看个人微博、好友微博、收藏微博等信息,对微博的操作有点赞,转发,收藏,评论,删除等。
    4.2.3 好友模块关注功能:当用户登陆系统后可以在页面右侧的推荐用户中选择需要关注的用户,或者是通过页面中的搜索功能来搜索用户,对搜索到的用户进行关注。关注用户后个人关注+1,同时被关注用户粉丝数+1。
    取消关注:和关注功能类型,在导航栏中点击我的好友,在我的好友首页中查看我的关注好友,就能查看到我所关注的所有好友和好友数以及他们的个人信息,通过点击对应的取消关注就可以取消关注该用户,取消关注用户后个人关注数-1,同时被关注用户粉丝数-1。
    拉黑用户:即修改好友表中对应的记录状态,被拉黑用户所发布的私信和微博信息不会被拉黑用户所看到。
    私信:可以在页面的推荐用户上面查看当前登陆用户的未查看私信数,未读私信的数目通过数据库中对所有接收者为当前登陆用户的所有私信信息,且信息状态为未阅读的私信,将得到的数值展示在前台页面中显示。
    发送私信:用户先选择要发送的用户,输入需要发送的私信信息,可以在私信中插入表情,点击发送后会将私信信息发送给对应用户。同时增加提示该用户的未读私信数。
    搜索用户:在搜索用户页面中,用户可以输入用户昵称的关键字来模糊查询相关用户,并将查询到的用户展示在页面中,同时将用户输入的关键字标红显示。
    用户主页面:在页面中,点击任意一个用户的名称或头像都会跳转至对应用户的个人主页,用户的个人主页显示用户的个人信息和用户最近发布的微博,按照时间倒序排列,用户也可以对微博的点赞、转发、评论、收藏做操作。
    4.2.4 海螺模块发布:用户通过点击导航栏中的神奇海螺进入海螺主页,在海螺页面的上面是发布海螺的问题框,下面的可以插入的表情按钮和问题的悬赏积分数,中间的导航栏可以选择查看最新发布、已解决、待解决、我的海螺不同的筛选条件。
    查看海螺问题:用户点击任意海螺问题,系统跳转到展示海螺的具体信息的页面,上方展示发布用户的用户名、用户称号、发布日期、海螺问题、悬赏积分、问题的状态等,在中间显示问题的所有回复信息,回复人的用户名、称号、回答内容、回复日期,是否被采纳等。
    回答海螺问题:在海螺问题详细详细信息页面的底部可以回答海螺问题,在输入框中输入回复的答案,插入表情信息等,点击回复后将回复信息保存在数据库中。
    采纳问题答案:在海螺问题首页,用户通过点击我的海螺问题可以跳转至用户自己所发布的海螺问题页面,在自己所发布的海螺问题页面中可以选择自己认为最正确的答案,点击采纳后修改海螺状态,被采纳的答案变为采纳答案,增加采纳者的海螺积分。
    4.2.5 后台管理员模块管理员登陆:管理员可以通过在登陆页面中点击管理员登陆,跳转至管理员登陆页面。当管理员输入账号信息和密码信息后,如果用户名和密码正确就跳转到管理员首页。如果错误则给出提示。
    管理员首页:在管理员首页中上方显示导航栏,在页面内容方面,通过四张图表来显示微博、用户、评、海螺、评论、回答等的总数以及当月数,在下方的柱状图中则显示距离今天最近的7天的数目。
    用户管理首页:在页面上方显示导航栏,页面内容中显示所有用户的编号、邮箱、昵称、注册日期、上次登陆日期以及用户的状态,由于考虑到用户数量多以以及为了方便查找,因此在页面中一次显示10条用户数据,同时添加用户搜索功能,用户能够在输入用户昵称的关键字后来搜索用户,并且支持迷糊查询。 搜索用户:在用户管理页面输入用户昵称中的关键字来搜索用户,系统会将获取到的用户信息中昵 称为输入关键字的那部分显示为红色。
    用户封禁:管理员可以通过点击用户管理页面操作一栏中的封禁按钮来对违规用户的封禁,管理员可以输入封禁的天数,那么在这个日期之前,用户是不能正常登陆的,管理员也可以通过点击解封来提前解除封禁用户的操作。
    微博管理首页:管理员点击导航栏中的微博管理,系统会跳转至用户微博管理页面,显示系统中所用是微博信息,管理员也可以通过输入微博内容中的关键字来搜索微博,同时可以删除有不良信息的微博,或是恢复以被删除的微博。
    微博搜索:管理员可以通过输入微博内容中的关键字来搜索在微博中存在该关键字的微博,搜索到的微博内容中的关键字会使用红色标注出来。
    微博删除:在微博管理页面中,管理员可以根据微博的内容来判断微博是否违法等信息,如果微博信息中包含不良信息,管理员可以通过操作栏中的删除按钮来删除微博或者可以对已经删除的为微博做恢复操作。
    海螺管理首页:管理员点击导航栏中的海螺管理可以跳转至海螺管理页面,在页面的上方为导航栏,页面内容则显示所有的海螺信息、海螺搜索框、以及下方的分页框,海螺信息包括编号、发布人名称、海螺的内容、发布日期、海螺状态以及可以执行的操作。
    海螺搜索:在海螺管理首页中,考虑到海螺数量多不好查找的问题,因此设置了搜索功能,管理员可以能够在海螺搜索框中输入海螺内容中的关键字来搜索海螺问题,在展示搜索到的结果时会将搜索关键字使用红色标注出来,方便查看。
    4.3 数据库设计4.3.1 概述微博系统数据库表主要包括:

    1. 用户表:用来保存用户的个人信息,例如编号、昵称、姓名、密码。邮箱等信息,以用户编号作为主键,如表4-1
    2. 微博关注表:用来保存用户的关注用户信息,以关注ID作为主键,如表4-2所示
    3. 微博表:用来保存微博信息,例如微博发布时间,微博内容,微博点赞,转发,收藏数等,以微博编号作为主键,如表4-3所示
    4. 微博收藏表:用来保存用户收藏的微博信息,如微博编号,收藏时间等,以收藏编号作为主键,如表4-4所示
    5. 评论表:保存微博的评论消息,例如评论人编号、评论日期、评论内容等,以评论编号作为主键,如表4-5所示
    6. 积分表:用来保存用户获取积分的方式,例如通过每天的登陆发布微博等获取积分,以积分编号作为主键, 如表4-6所示
    7. 点赞表:用来保存微博的点赞信息,主要包括点赞人编号,点赞编号,点赞微博编号等信息。以点赞编号作为主键,如表4-7所示
    8. 积分流水表:用来保存用户获取积分的信息,主要包括积分编号、用户编号、获取日期等信息,以积分编号作为主键,如表4-8所示
    9. 转发表:用来保存用户转发的信息,主要有转发编号、转发人、微博编号等信息,以转发编号作为主键,如表4-9所示

    4.3.2 概念设计一个用户可以发布多条微博,因此用户表和微博表之间存在一对多的关系如图4.12所示。

    一条微博可以对应多个点赞、转发、收藏和评论,因此微博表和收藏、点赞、转发、收藏表之间存在一对多的对应关系,如图4.13所示。

    一个用户可以发布多个海螺,每个海螺问题可以对应多条评论。如图4.14所示。

    4.3.3 数据库表
    用户表:数据库表名USER_TAB,引用序列名SEQ_USER。


    关注表:数据库表FRIEND_TAB,引用序列名SEQ_FRIEND。


    微博表:数据库表名WEIBO_TAB,引用索引名SEQ_WEIBO。


    收藏表:数据库表名COLLECT_TAB,引用索引名SEQ_COLLECT。


    评论表:数据库表名COMM_TAB,引用索引名SEQ_COMM。


    积分表:数据库表名INTEGRAL_TAB,引用索引名SEQ_INTEGRAL。


    点赞表:数据库表名LIKE_TAB,引用索引名SEQ_LIKE。


    积分流水表:数据库表名RECORD_TAB,引用索引名SEQ_RECORD。


    转发表:数据库表名TRANS_TAB,引用索引名SEQ_TRANS。

    第五章 系统功能实现5.1 登陆系统在系统登陆注册页面当用户输入邮箱后会通过Ajax将用户输入的邮箱传到后台控制器,调用Service层中对应的方法,是Service方法中调用Dao层接口查找用户邮箱是否已经被注册,如果被注册则通过前台javaScript显示在页面提示用户该邮箱已被注册,在输入基本信息点击注册后,将会把用户输入的注册信息通过浏览器发送请求到后台控制器中,控制器控制请求的转发页面和将用户注册信息传给Service,在Service中初始化用户的一些基本信息,例如默认头像、默认状态、初始化用户积分等操作,组装用户数据源,调用Dao层方法保存用户注册信息如图5.1所示。核心代码如下:

    登陆功能:如果点击下次自动登陆密码在点击登陆后,先将用户名和密码信息传递到Service层做业务处理,再调用Dao层接口判断邮箱地址和密码信息是否正确,如果正确并且点击了下次登陆,就将邮箱名和密码保存在浏览器Cookie中,将登陆用户保存在Session中,在处理用户登陆的Service中判断用户的当前登陆时间和上次登陆时间是否为同一天,如果不是同一天则为用户增加登陆的积分,修改用户数据库中的积分数,然后跳转至微博首页,如图5.2所示。核心代码如下:



    5.2 用户信息系统用户基本信息:如图5.3所示当用户点击修改后浏览器会提交form表单,发送请求携带用户基本信息到SpringMVC的控制器中接收请求,并把form表单中的数据组装成User对象,作为参数传入Service层中对应的方法进行处理,调用Dao层接口修改用户的基本信息,并修改当前服务器中Session中的User对象信息。核心代码如下:


    修改密码:在用户个人资料页面点击修改密码,将会跳转至如图5-4所示的修改密码页面,用户可以输入用户的当前密码,前台页面通过Ajax发送异步请求,后台控制器接收请求,从数据库中获取用户当前密码是否正确,如果密码不正确,在前台页面中通过javaScript动态提示给用户当面密码错误,如果输入密码正确,用户则可以输入新密码,确认新密码,点击确认修改后浏览器提交form表单,将用户新密码传给后台Service层中对应的方法,在Service的方法中调用Dao层接口更改数据库中的用户密码,更改服务器Session中的用户信息。核心代码如下:


    修改用户头像:点击用户个人资料中修改头像,将会跳转至如图5.5所示的修改头像页面,用户选择头像文件,点击上传,form表单将图片传到后台控制器中,将用户头像保存在服务器上,判断用户之前头像是为系统默认头像,如果不是就删除用户之前的头像图片,并将用户新的头像地址保存在用户信息中,传入Service中调用Dao层接口修改数据库中用户的头像信息。核心代码如下:


    修改密保:当用户点击修改密保页面时浏览器跳转至修改密保页面如图5-6所示,用户先要根据之前设置的密保问题来填写答案,前台页面通过Ajax将用户输入的密保答案传入后台控制器,与数据库中用户的密保问题答案做判断,如果密保答案错误,在页面上通过javaScript展示用户密保答案错误,如果密保答案正确,用户可以输入新的密保问题和密保答案,点击修改提交form表单后,浏览器发送请求在后台数据库中修改用户的密保问题和答案。核心代码如下:


    5.3 微博模块发布微博:在用户主页如图5.7所示,用户可以发布微博,在微博信息中可以插入表情,也可以选择插入一张图片,在前台页面中,表情使用javaScript动态生成div标签并显示在页面上,当用户点击发布后,浏览器发送请求将前台页面表单中微博信息和图片信息传入后台控制器,将图片信息保存在服务器中,在数据库中只保存图片路径,从Session中获取当前登陆用户,组装微博数据源,将数据源传入Service中,调用Dao层接口将微博信息保存在数据库中。核心代码如下:


    搜索微博:如图5.8所示,在微博首页搜索框输入关键字点击搜索提交form表单,浏览器发送请求将关键字传到后台控制器中,在数据库中通过迷糊查询查询相关的微博信息。在Service层中具体方法中调用Dao层接口获得相关的微博信息,遍历集合,组装微博的基本信息和发布人对象,将微博中表情转换成对应的gif图片,调整微博时间格式为对应格式,修改微博内容中搜索关键字为红色显示,将修改好的数据返回到前台页面展示,如图5.9所示。核心代码如下:


    微博操作:点击微博的点赞,转发和收藏功能类型,通过Ajax将微博id提交请求到后台控制器,从Session中获取登陆用户的信息,传递参数到Service对应的方法中通过对微博不同的操作调用对应的Dao层接口将微博的点赞,转发和收藏信息保存在数据库中。点击评论后通过前台页面的点击事件跳转至JavaScript中通过Ajax发送异步请求到后台控制器中,将微博id传递到Service层中对应的方法中嗲用Dao层接口查找数据库中对应微博编号的评论信息,微博评论的分页通过对应的PageBean类控制,在数据库层通过SQL语句来控制分页要显示的条数,在控制器中传递json数据到前台页面展示。在评论信息的最后面可以发布用户自己的评论,可以添加表情,点击发表将form表单提交到后台控制器中,在后台代码中调用Dao层接口保存用户的评论信息。如图5.10所示。核心代码如下:


    个人微博:如图5.11所示:在页面上方导航栏可以点击进入用户个人主页查看我的微博,在页面中页面上方展示导航栏,下面显示用户的基本信息,中间显示用户所发布过的微博,点击微博插入的图片还可以通过JavaScript将图片放大,在页面的右侧展示系统为用户推荐的好友。点击我的微博按钮后发送的请求会被后台控制器接收,从服务器Session中获取当前登陆用户的用户id,传入Service层,在Service层中调用Dao层接口从数据库中获取当前登陆用户的微博信息组装成List集合,遍历List集合,更改微博数据源的日期格式,调用工具类将微博正文中的表情替换成对应的图片信息,在微博的分页中,使用了Oralce数据库中的伪列来获取对应区间的微博信息,实现数据库层的分页,将所有需要在页面上展示的数据传递给控制器,控制器再将数据封装在Response响应中传递到前台页面。核心代码如下:


    5.4 好友模块查看好友微博:通过点击页面导航栏中我的好友来跳转至好友微博页面,在后台控制器中先从Session中获得当前登陆用户的id值,传递给Service,在Service中调用Dao层方法先在数据库的好友表中查找对应用户的所有好友信息,然后再在数据库微博表中查看这些好友对应的微博信息按照微博发布时间倒序排列,将所有查找到的微博信息和发布用户信息封装成List集合,遍历List集合修改微博日期各式,微博表情字符转换为对应图片名称,将加工过后的集合传递给控制器,控制器将所有的组装好的数据响应到前台页面中,在前台页面中通过C标签遍历List展示微博数据如图5.12所示。核心代码如下:

    关注功能:当用户登陆系统后可以在页面右侧的推荐用户中选择需要关注的用户,点击关注后前台页面通过Ajax技术发送异步请求将被关注用户的用户id发送到后台控制器中,在控制器中获取Session中的登陆用户信息,将登陆用户的id和被关注用户的id传给Service层中对应的方法,在Service层对应的方法中做处理,组装好友信息数据源,调用Dao层接口保存用户的关注信息,同时更新用户的关注数,更新被关注用户的粉丝数,然后在前台页面中通过javaScript将页面文本信息关注修改为已关注。核心代码如下:

    取消关注:和关注功能类型,在导航栏中点击我的好友,在中间点击关注,就能查看到我所关注的所有好友和好友数以及他们的个人信息,如图5.13所示,点击取消关注,通过Ajax将好友id传递到后台控制器中,在控制器中获取请求中的用户信息,将好友id和用户id传入Service层中对应的方法,调用Dao层接口将对应用户id和好友id的好友信息删除掉,同时更改用户的关注数和被关注用户的粉丝数。然后返回响应在前台页面刷新好友列表。

    拉黑用户:和取消关注用户类似,只是在Dao层接口中,不是删除已有的用户记录而是逻辑删除,即修改好友表中对应的记录状态,被拉黑用户所发布的私信和微博信息不会被拉黑用户所看到。核心代码如下:


    私信:可以在页面的推荐用户上面查看当前登陆用户的未查看私信数,如图5.14所示,未读私信的数目通过数据库中对所有接收者为当前登陆用户的所有私信信息,且信息状态为未阅读的私信,将得到的数值传递给前台页面,前台页面中所有展示的数值通过EL表达式从服务器发送回来的响应中获取。点击未读私信或者通过图5.13中显示的好友列表中的私信按钮,跳转至用户私信页面。核心代码如下:


    查看私信:如图5.15所示,用户通过点击私信关注好友时,通过后台控制器跳转至我的私信用户页面,页面左侧显示和该用户最近的私信信息,页面右侧显示所有有过私信的用户列表,点击列表中的用户就可以直接私信这个用户,在私信中允许插入表情。在后台代码中,将要发送私信的用户id和从Session中获取的登陆用户id传递到Service层中对应的方法,调用Dao层接口在数据库中查找与该用户相关的所有私信记录同时修改和该用户的所有私信状态为以阅读,将获取的记录组装到集合中,遍历集合调用工具类修改私信的日期格式和文本中的表情格式,同时从数据库中获取和当前登陆用户有过私信记录的所有用户,将组装好的集合返回给控制器,控制器将数据响应给前台页面中,遍历集合中的数据,展示在前台JSP页面中。核心代码如下:


    发送私信:用户先选择要发送的用户,输入需要发送的私信信息,可以在私信中插入表情,点击发送后会提交form表单,浏览器发送请求到后台控制器中,控制器获取发送用户的id和接收用户的id,调用Service层中的方法,组装数据源为私信信息,设置私信信息为未阅读状态,在Service层中对应的方法中调用Dao层接口将私信信息保存在数据库中。核心代码如下:

    搜索用户:在搜索用户页面中,用户可以输入用户昵称的关键字来模糊查询相关用户,当用户输入要查询的用户昵称,浏览器发送请求携带关键字等信息跳转至控制器中特定的方法,在控制器方法内部调用Service中的方法处理逻辑,业务层调用Dao层接口中的查找方法查找用户昵称中包含有关键字的用户,将从数据库获得的对应用户组装成一个集合,遍历集合将用户昵称中包含的关键字改成红色,控制器返回响应跳转到搜索结果页面,遍历集合展示所有查找到的用户信息。如图5.16所示。核心代码如下:


    用户主页面:在页面中,点击任意一个用户的名称或头像都会跳转至对应用户的个人主页,用户的个人主页显示用户的个人信息和用户最近发布的微博,按照时间倒序排列,用户也可以对微博的点赞、转发、评论、收藏做操作。在后天代码中,当用户点击其他用户的头像或名称时,浏览器发送携带用户id的请求到后台控制器中。控制器调用Service中对应的方法,在Service方法中调用Dao层接口从数据库中查看用户的基本信息和用户的微博信息,将所有的数据存放在集合中。返回到控制器中,控制器携带数据返回到前台页面中做展示,如图5.17所示。核心代码如下:


    5.5 海螺模块发布:当用户进入海螺主页时,浏览器页面发送请求到后台控制器中,调用业务层中特定的方法,Service中调用Dao层接口在数据库中查找有关不同筛选条件的海螺问题,并将查找到的问题封装在集合中,通过控制器发送服务器响应,跳转到海螺首页,并循环展示所有的海螺问题。如果用户需要发布问题,在填写了问题描述和所要悬赏的积分数后,点击发布,浏览器提交表单数据到后台控制器中,在Service层中首先判断用户的海螺积分是否大于悬赏积分,如果小于悬赏积分就返回浏览器页面提示用户积分不足。如果积分足够就调用Dao层接口把用户的海螺问题保存在数据库中,同时减少用户的海螺积分修改用户基本信息。核心代码如下:


    查看海螺问题:用户点击海螺首页具体的问题时,浏览器发送请求给后台控制器中调用Service层对应的方法,在Service层中调用Dao层接口通过海螺问题的编号来查看海螺的具体信息,通过发布问题的用户id在数据库中查找对应的发布人信息,服务器返回响应到浏览器中,展示海螺的具体信息,上方展示发布用户的用户名、用户称号、发布日期、海螺问题、悬赏积分、问题的状态等,在中间显示问题的所有回复信息,回复人的用户名、称号、回答内容、回复日期,是否被采纳等,如图5.19所示。核心代码如下:


    回答海螺问题:在海螺问题详细详细信息页面的底部可以回答海螺问题,在输入框中输入回复的答案,插入表情信息,点击回复提交表单,浏览器请求携带表单数据到后台控制器中,被控制器中具体的方法接收,获取Session中的用户编号,组装回复信息的数据源,传递参数到Service中,在Service中调用Dao层接口保存海螺的回复信息,如图5.20所示。核心代码如下:


    采纳问题答案:在海螺问题首页,用户通过点击我的海螺问题可以跳转至用户自己所发布的海螺问题页面,在自己所发布的海螺问题页面中可以选择自己认为最正确的答案,被采纳的答案变为采纳答案,增加采纳者的海螺积分,如图5.21所示,在后台代码中,当用户点击采纳后,浏览器请求携带海螺问题id和问题回复信息被后台控制器中具体的方法接收,在控制器中调用Service层,业务层中调用Dao层接口修改数据库中海螺问题的状态为已解决,修改评论表中被采纳用户的评论状态为被采纳,刷新前台海螺问题页面。核心代码如下:


    5.6 后台管理员模块管理员登陆:管理员可以通过在登陆页面中点击管理员登陆,跳转至管理员登陆页面如图5.22所示,管理员登陆后,页面发送请求到后台控制器中,后台Controller层接收请求,将用户名和密码作为参数调用业务层中的方法,在Service层中调用Dao层接口和数据库中管理员账号表中查询,如果存在就返回管理员类,如果不存在,则抛出异常,异常层层上抛,在控制器层中接收,并将错误信息保存在方法的返回值中,在页面提示,如果用户名和密码正确,就跳转到Controller中管理员首页的处理方法中,在管理员首页的处理方法中获取需要显示的数据并展示。核心代码如下:


    管理员首页:登陆后,系统会跳转到管理员首页,在首页中上方显示导航栏,在页面内容方面,通过四张图表来显示微博、用户、评、海螺、评论、回答等的总数以及当月数,在下方的柱状图中则显示距离今天最近的7天的数目。后台首先在Controller层中跳转至管理员首页对应的处理方法中,调用Service层中对应的方法获取首页展示数据,并将获取到的数据组装到Map集合中,在服务层中调用Dao层中的方法来获取首页需要展示的用户、微博、评论、回答等数据保存在Map集合中,在控制层中获取到返回值数据并保存在服务器响应中,返回给前台页面使用EL表达式展示数据,如图5.23所示。核心代码如下:


    用户管理首页:后台代码通过调用Service层中对应的方法,Service方法里面则调用Dao层接口和数据库交互,获取数据库中所有的用户信息封装成List集合,返回给Controller层将用户List集合响应给浏览器,在页面中通过c标签遍历循环显示用户信息,页面的分页使用自定义分页类PageBean来保存分页信息,在数据库层做分页一次获取10条数据。核心代码如下:


    搜索用户:在用户管理页面输入用户昵称中的关键字来搜索用户,系统会将获取到的用户信息中昵称为输入关键字的那部分显示为红色。在后台代码中,点击搜索会将管理员输入的用户昵称关键字传给后台控制器中,在控制器中调用Service层中对应的搜索用户的方法,在Service层中调用Dao层接口在数据库中通过迷糊查询来获取用户。并将获取到的数据层层返回,在Controller中响应给前台页面,然后在页面中做展示,如图5.25所示。核心代码如下:


    用户封禁:管理员可以通过点击用户管理页面操作一栏中的封禁按钮来对违规用户的封禁,管理员可以输入封禁的天数,那么在这个日期之前,用户是不能正常登陆的,管理员也可以通过点击解封来提前解除封禁用户的操作,具体页面如图5.26所示。在后台代码中则是在Service层中调用Dao层接口,更改用户状态和封禁日期,解除封禁和封禁用户类似,因此不做具体说明。核心代码如下:


    微博管理首页:管理员点击导航栏中的微博管理,系统会跳转至用户微博管理页面,显示系统中所用是微博信息,管理员也可以通过输入微博内容中的关键字来搜索微博,同时可以删除有不良信息的微博,或是恢复以被删除的微博,微博管理页面如图5.27所示,在后台代码中,当用户点击导航栏中微博管理时,浏览器会发送相应的请求到SpringMVC框架的Controller中对用的更能处理方法中,在控制器中调用Service层中显示所有微博的方法中,在Service层中再调用Dao层方法获取所有的微博信息,在分页方面没有采用在前端页面中做分页的方法,而是在数据库中通过Oracle的伪列来做分页,一次获取10条数据,最后在Controller中将获取到的微博信息响应给浏览器,浏览器中通过c标签遍历显示微博信息。核心代码如下:


    微博搜索:管理员可以通过输入微博内容中的关键字来搜索在微博中存在该关键字的微博,搜索到的微博内容中的关键字会使用红色标注出来,具体页面如图5.28所示,在后台代码中,控制器中特定的方法接收浏览发送的搜索微博请求,调用Service层中对应的方法,将关键字作为参数传给Dao层接口中,在Dao层接口中查找数据库微博表中微博内容包含该关键字的微博信息,在数据库中则是通过模糊查询来查找对应微博。然后将查找到微博信息封装到List集合中,层层返回到Controller层中对应的方法,在方法中将数据响应给浏览器,浏览器接收响应在页面中通过c标签展示数据。核心代码如下:


    微博删除:在微博管理页面中,管理员可以根据微博的内容来判断微博是否违法等信息,如果微博信息中包含不良信息,管理员可以通过操作栏中的删除按钮来删除微博或者可以对已经删除的为微博做恢复操作,当用户点击删除时,页面会携带着微博编号等参数发送请求给服务器,请求会被控制器中对应的方法所接收,将微博编号作为参数嗲用Service中对应的方法,在方法中调用Dao层接口在修改数据库中对应微博编号的微博状态为已删除,恢复微博和删除类似,只是修改数据库中对应微博信息状态为正常即可。核心代码如下:

    海螺管理首页:管理员点击导航栏中的海螺管理可以跳转至海螺管理页面,如图5.29所示,在页面的上方为导航栏,页面内容则显示所有的海螺信息、海螺搜索框、以及下方的分页框,海螺信息包括编号、发布人名称、海螺的内容、发布日期、海螺状态以及可以执行的操作,在后台代码方面,当用户点击导航栏中的海螺管理时,浏览器发送请求,请求在控制器中被对应的方法接收,Service中调用Dao层接口在数据库海螺表中查找所有的海螺信息,保存在List集合中,遍历List集合组装发布人用户信息,修改日期格式等,最后将组装号的List集合返回到Controller中,响应给浏览器跳转海螺管理首页,通过使用c标签遍历显示查找到的海螺信息,因为系统中使用的分页方法一致,因此在这里不再赘述。核心代码如下:


    海螺搜索:在海螺管理首页中,考虑到海螺数量多不好查找的问题,因此设置了搜索功能,管理员可以通过在海螺搜索框中输入海螺内容中的关键字来搜索海螺问题,在展示搜索到的结果时会将搜索关键字使用红色标注出来,方便查看。具体页面如图5.30所示。在后台代码中,当用户输入关键字点击搜索时,浏览器发送请求到控制器中指定方法接收,在Controller中调用Service层中对应的方法处理业务逻辑,然后在Service层中调用Dao层接口通过模糊查询在数据库海螺表中查找对应的海螺信息,最后在Controller中将查找到的海螺信息响应给浏览器,在页面中通过c标签遍历展示数据。因为系统使用一样的分页方法,因此不做赘述。核心代码如下:


    第六章 总 结时光荏苒,岁月如梭,转眼之间为期半年的毕业设计以及论文的编写终于落下帷幕,回顾毕业设计的每一个阶段都使我受益匪浅,在毕设初期,经过了长达一周的深思熟虑之后我决定将B/S微博系统作为我这次毕业设计和论文的选题,因为平时对微博的接触以及近几年微博的火热程度,使我对微博系统的具体功能和优缺点都有了一个全体的把控,这也使得在之后系统功能设计时能够更加的得心应手,在毕业设计中期时进入代码编写阶段时,我选择了J2EE体系开发Web项目的B/S微博,使用面向对象语言JAVA作为开发语言,使用个人熟练掌握的SSM框架来搭建系统,SSM框架强大的功能减少了大量的冗余代码,使得系统代码的编写更加轻松,提高了系统的开发效率,然而开发一个系统并不是那么简单就能完成,在代码的编写阶段,问题接连出现,但在指导老师的指导下以及自己通过网上查阅资料,最终解决了这些问题,提高了自己代码的编写能力,同时也提高了自我学习能力,这对以后的生活学习和工作中都有着非同寻常的意义,在项目编写完成后的测试阶段,之前在编写阶段没有被发现的系统缺陷逐渐跃出水面,在每一次修复这些问题的时候,我都能感觉到自己的能力在慢慢提高,一个没有缺陷的系统是不存在的,经过对测试用例中的测试方案一一测试,然后修复掉一个又一个的缺陷后,微博系统中大多数可见性高的BUG都被修复了,但是在之后的使用中还会不断的完善系统让它变的更加成熟,而不单单只是作为一份毕设设计而存在。
    在这次毕业设计系统的开发中也让我看到了自身的一些问题,例如前端技术的不熟练导致在前台页面的修改和开发中浪费了大量的时间,微博系统中关键性的用户交互不够美观,用户体验性差,以及在系统类设计时没能正确的把控全局,设计出合理的接口,只能在后期代码编写阶段中不断的去完善。
    总而言之,在对微博系统的开发中,我学习到了很多以前没有注意到和忽略到的东西,也使我认识到了自身的一些缺陷,让我在以后的生活和工作中都能更好的认识自己,提高自己的能力,然后服务于社会,做一个对社会发展有帮助的人。
    参考文献[1] 贾文潇,邓俊杰. 基于Java的Web开发技术浅析[J]. 电子测试,2016
    [2] 李传扬. 微博分析系统的设计与实现[D]. 北京邮电大学 2015
    [3] 刘运臣. 网站设计与建设[M]. 清华大学出版社, 2008
    [4] 秦雅华. 基于WEB2.0的微博网站的设计与实现[D]. 北京工业大学 2012
    [5] 陈玲,夏汛. 利用Mybatis的动态SQL实现物理分页[J]. 数字技术与应用. 2011(11)
    [6] 萨师煊,王珊. 数据库系统概论(第三版)[M].北京:高等教育出版社,1998
    [7] 基于Java的数据库访问技术研究[J]. 科技资讯. 2009(04)
    [8] 张峰. 基于Ajax技术与J2EE框架的Web应用研究与实现[D]. 中国地质大学 2008
    [9] 基于Java多线程技术的网络编程[J]. 电脑编程技巧与维护. 2009(22)
    [10] 李威. 一种小型实用即时网络聊天通讯系统的设计[J]. 长江大学学报(自然科学版). 2011(12)
    [11] 钟睿祺. 基于微博嵌入小伙伴阅读网的分析与设计[D]. 华南理工大学 2011
    [12] 王少锋编著.面向对象技术UML教程[M]. 清华大学出版社, 2004
    [13] 徐春绵. 关于网站开发相关问题的探究[J]. 通讯世界. 2015(09)
    [14] 张宇,王映辉,张翔南. 基于Spring的MVC框架设计与实现[J]. 计算机工程. 2010(04)
    [15] 胡以谰,张立平. J2EE开发模式的选择[J]. 计算机系统应用. 2002(08)
    [16] 王丽爱. 《Java程序设计》课程网站的设计与实现[J]. 电脑知识与技术. 2016(27)
    [17] 荣艳冬. 关于Mybatis持久层框架的应用研究[J]. 信息安全与技术. 2015(12)
    12 留言 2019-12-20 17:48:45 奖励50点积分
  • python实现随机产生10个10~99之间的整数

    随机产生10个10~99之间的整数实现以下操作:

    将十个数存入列表,输出列表原始值
    将列表进行递增排序并输出
    将列表反序(即按照递减排序)并输出
    输出列表中最大值,最小值
    输出列表中元素的平均值

    代码如下:
    import randomlist1=[]for i in range(10): list1.append(random.randint(10,99))print (“随机生成的整数列表为:”,list1)list1.sort()print (“递增排序后的列表为:”,list1)list1.reverse()print (“反序后的列表为:”,list1)print(“列表元素的最大值为:”,max(list1))print(“列表元素的最小值为:”,min(list1))sum=0for i in list1: sum+=iprint(“列表元素的平均值为:”,sum/len(list1))````python
    0 留言 2021-09-06 22:17:28 奖励20点积分
  • DOOR-SLAM:用于机器人团队的分布式、在线和异常值拒绝的SLAM

    DOOR-SLAM: Distributed, Online, and Outlier Resilient SLAM for Robotic Teams摘要为了实现协作任务,机器人团队需要共享对环境的感知和定位信息。分布式SLAM提供了一个实际的解决方案,可以不依赖外部定位系统(例如GPS)定位机器人,并且最小化信息交换。不幸的是,目前的分布式SLAM系统易受感知外点影响,因此对于机器人之间的位置识别通常采用非常保守的参数。然而,过于保守会导致拒绝掉许多有效的回环候选,这个会导致轨迹估计更低的精确度。本文提出了DOOR-SLAM,一个完全分布式的SLAM系统,有着一套使用更少的保守参数的有效的外点拒绝机制。DOOR-SLAM是基于点对点通信,并且不要求机器人之间全部连接。DOOR-SLAM包括两个关键的模块,一个是位姿图优化器与分布式成对一致测量集最大化算法相结合,以拒绝虚假的机器人之间的回环;另一个是一个分布式SLAM前后端不交换原始的传感器数据可以检测机器人内部的回环。该系统在模拟器上、标准数据集和试验区域,例如GPS信号弱的地下环境,进行了评估。DOOR-SLAM产生了更多的机器人间回环,成功拒绝了外点,并且产生了精确的轨迹估计,同时请求低的通信带宽。完整的代码是可以在https://github.com/MISTLab/DOOR-SLAM.git获得的。
    主要工作与贡献

    DOOR-SLAM是一个用于机器人团队的完全分布式 SLAM 系统。DOOR-SLAM 具有以下理想特性:(i)它不需要机器人之间的完整连接维护,(ii)它能够在不交换原始数据的情况下检测机器人间的闭环,(iii)它执行分布式异常值拒绝以删除不正确的机器人间闭环,并且 (iv) 它执行分布式姿态图优化以检索机器人的轨迹估计。提议的系统包括两个关键模块。第一个模块是对虚假测量具有鲁棒性的位姿图优化器。我们提出了一种分布式姿态图优化的实现,沿着分布式 Gauss-Seidel 算法的路线结合基于PCM的异常值拒绝机制,我们适用于在线和分布式操作。所提出的模块提供的鲁棒性示例如图 1 所示,它报告了带有和不带有异常值拒绝的轨迹估计。我们的实现对感知混叠具有鲁棒性,并允许从业者使用不太保守的 SLAM 前端调整。第二个模块是数据高效的分布式SLAM 前端。我们的系统使用 NetVLAD 描述符进行位置识别。然而,我们的方法牺牲了一些数据效率,以避免完全连接维护和特定于环境的预训练要求。DOOR-SLAM 已经在模拟、基准数据集(KITTI )和现场实验中进行了评估,包括在拒绝 GPS 的地下环境中的测试。DOOR-SLAM 在 NVIDIA Jetson TX2 计算机上在线运行,成功拒绝异常值,并产生准确的轨迹估计,同时需要低带宽。我们发布了源代码和 Docker 镜像,以便社区轻松重用系统组件:https://github.com/MISTLab/DOOR-SLAM.git。
    算法流程1.系统预览我们的分布式 SLAM 系统依赖于点对点通信:当通信范围内没有队友时,每个机器人执行单机器人 SLAM,并在集合期间执行分布式 SLAM 协议。我们的实现利用了 Buzz,这是一种专为多机器人系统设计的编程语言。Buzz 提供有用的原语来构建完全分散的软件架构,并无缝处理单机器人和多机器人执行之间的转换。Buzz 是一种脚本语言,可以让我们抽象出有关通信、邻居检测和管理的细节,并提供统一的框架来实现和比较多机器人算法(如 SLAM、任务分配、探索等)。它提供了一个统一的gossip-based 的接口,在 WiFi、Xbee、蓝牙或自定义网络设备上实现。Buzz 被认为是一种扩展语言,即它旨在放置在其他框架之上,例如机器人操作系统 (ROS)。这使我们能够在几乎任何类型和任意数量的支持 ROS 的机器人上运行 DOOR-SLAM,无需修改。Buzz的实验表明 Buzz 可以扩展到数千个机器人。
    图 给出了 DOOR-SLAM 的系统概述。每个机器人从车载立体相机收集图像,并使用(单机器人)立体视觉里程计模块来估计其轨迹。在我们的实现中,我们使用来自 RTAB-Map的立体里程计。图像也被馈送到分布式环路闭合检测模块与其他机器人(当它们在通信范围内时)通信并输出机器人间环路闭合测量值。然后,分布式异常值拒绝模块收集里程计和机器人间测量值,以计算成对一致测量值的最大集合并过滤掉异常值。最后,分布式姿态图优化模块执行分布式 SLAM。为简单起见,在当前的实现中,我们只考虑机器人间闭环(即,涉及不同机器人姿势的闭环)。通过用视觉 SLAM 解决方案替换立体里程计,该系统可以轻松扩展为使用机器人内部闭环(即单机器人 SLAM 中常见的闭环)。
    2.分布式环路闭合检测分布式闭环检测包括两个子模块。第一个子模块,位置识别,允许使用紧凑的图像描述符找到闭环候选。第二个子模块,几何验证,计算观察同一场景的两个机器人姿态之间的相对姿态估计。该过程上图所示。地点识别子模块依赖于 NetVLAD 描述符,它对视点和光照变化紧凑且鲁棒。每个机器人在本地计算立体视觉里程计模块提供的每个关键帧的 NetVLAD 描述符。一旦两个机器人(α 和 β)在通信范围内,其中一个(α)将NetVLAD 描述符发送给另一个(β)。机器人 α 仅发送自两个机器人上次相遇后生成的描述符,或者如果这是它们的第一次约会,则发送所有描述符。机器人 β 将接收到的 NetVLAD 描述符与其从自己的关键帧生成的描述符进行比较。通过这样做,机器人 β 选择与欧几里德距离低于给定阈值的关键帧对相对应的潜在闭环。此过程提供了假定的闭环,而无需交换原始数据、完整的连接维护或额外的特定于环境的预训练。每个机器人还从立体对的左图像中提取视觉特征、相关的特征描述符以及它们对应的估计 3D 位置;这些由几何验证子模块使用。在找到一组假定的闭环后,机器人 β 将视觉特征及其描述符和 3D 位置发送回机器人 α。这对假定的闭环中涉及的每个关键帧进行。使用这些特征,机器人 α 使用 OpenCV 中的 solvePnpRansac 函数执行几何验证,该函数返回一组内部特征和相对位姿变换。如果内点集足够大,机器人 α 认为相应的回环成功。最后,机器人 α 将与成功闭环对应的相对位姿传回给机器人 β。一旦找到并共享机器人间闭环,两个机器人就会启动下一节中描述的分布式鲁棒姿态图优化协议。
    3.分布式鲁棒 PGO该模块负责根据立体视觉里程计模块的里程计测量值和分布式闭环检测模块的相对位姿测量值来估计机器人的轨迹。该模块还包括一种分布式异常值拒绝方法,该方法可以消除可能意外通过上一节中描述的几何验证步骤的虚假环路闭合。分布式 PGO 子模块使用里程计测量和内部机器人间闭环来计算机器人的轨迹估计。我们使用他人提出的方法-分布式 Gauss-Seidel 算法:机器人反复交换他们对机器人间闭环中涉及的姿势的估计,直到他们就最佳轨迹估计达成共识。更具体地说,分布式 Gauss-Seidel 算法使用两阶段方法以分布式方式解决姿态图优化问题:首先,它计算机器人沿其轨迹旋转的估计;然后它在第二阶段恢复完整的姿势。每个阶段都可以使用分布式 Gauss-Seidel 算法解决,该算法避免了复杂的簿记和信息重复计算,并且需要最少的信息交换。
    0 留言 2021-09-02 17:25:13 奖励50点积分
  • 【兼职招聘】WRITE-BUG实习生招聘



    一、兼职招聘计划write-bug技术共享平台( www.write-bug.com )致力于设计、研发、运营一个帮助学生成长的校园开源社区,目前拥有注册用户数 10 万,创始团队均由清华、人大等硕博在校学生组成。
    兼职岗位:

    新媒体运营实习生
    前端开发实习生
    后端开发实习生

    二、招聘条件
    新媒体运营实习生(3~5名):

    身心健康,爱岗敬业,政治素质高,道德品行端正,遵纪守法,无违法犯罪和不良行为记录专科及以上学历的在校学生,专业不限,大二、大三学生优先实习时间灵活,课余时间负责 WRITE-BUG 在微信、知乎、B站等平台上的互动、答疑熟练使用知乎、B站等平台优先支持现场(北京)和远程两种实习方式
    前端开发实习生(2~3名):

    身心健康,爱岗敬业,政治素质高,道德品行端正,遵纪守法,无违法犯罪和不良行为记录专科及以上学历的在校学生,计算机相关专业,大二、大三学生优先实习时间有保障,原则上实习时间不少于 2 个月(每周有 2 天以上出勤)要求了解/熟悉 VUE 主流框架支持现场(北京)和远程两种实习方式
    后端开发实习生(2~3名):

    身心健康,爱岗敬业,政治素质高,道德品行端正,遵纪守法,无违法犯罪和不良行为记录专科及以上学历的在校学生,计算机相关专业,大二、大三学生优先实习时间有保障,原则上实习时间不少于 2 个月(每周有 2 天以上出勤)要求了解/熟悉 Python 开发,了解/熟悉 Django 框架支持现场(北京)和远程两种实习方式

    三、实习待遇
    贴心导师辅导,有专业项目导师指导相关前端、后端开发及平台运营工作,技术上可得到较大锻炼和提升的机会
    实习鉴定优秀的,将开具相关实习证明,并酌情邀请加入项目初创团队
    支持现场(北京)和远程两种实习方式

    一楼:环境清幽的高层办公环境,可同时容纳 10 人办公,WIFI、投影、打印、冰箱等设备一应俱全二楼:干净整洁的 2 个休息房间,床铺、被褥、枕头、拖鞋等用品全都备齐,拎包入住
    实习津贴面议









    (一楼办公环境全景)





    (二楼房间环境全景)



    四、招聘流程
    网申投递

    截止时间:长期有效
    简历投递方式

    点击本站顶部导航栏 实习-简历投递 按钮上传个人简历,标题格式:【实习】+姓名+学校+年级+应聘职位
    资格审核

    WRITE-BUG 将根据招聘条件对应聘者进行资格审查,并根据岗位需求及报名情况等,择优甄选确定入围面试人员。

    面试及实习等后续工作具体安排另行通知,请及时关注。
    3 留言 2021-09-13 08:06:44 奖励100点积分
  • 基于TDI实现的网络通信

    1.概述在Windows操作系统下提供了两种网络编程模式,分别为用户模式和内核模式。顾名思义,用户模式是主要是通过调用用户层的API接口函数实现的用户程序,内核模式主要是通过调用内核层的内核API函数接口或是自定义构造通信协议实现的内核驱动程序。用户模式虽然易于开发,但是容易被监控;内核模式实现较为复杂,但是实现更为底层,通信更为隐蔽,较难监控。
    内核模式下的网络编程,主要是通过传输数据接口TDI(Transport Data Interface)和网络驱动接口规范NDIS(Network Driver Interface Specification)实现的。TDI是直接使用现有的TCP/IP协议来通信的,无需自己重新编写新的协议,所以,基于TDI开发的网络通信是能够被网络防火墙检测到的。而NDIS可以直接在网络上读写原始报文,需要自定义通信协议,能够绕过网络防火墙的检测。
    数据回传对于病毒木马来说是极为关键的一步,稍有差池,则会原形毕露。所以,内核下的网络通信,将会使病毒木马的通信方式更为底层隐蔽,难以检测。
    接下来,本文将介绍基于TDI实现的TCP网络通信,实现一个驱动客户端程序,能够与用户层的服务端程序建立TCP连接并使用TCP通信。
    2.实现过程前面用户篇的时候介绍过Socket编程之TCP通信的小程序,Socket编程中调用的API函数接口比较容易理解,然而,内核下的TDI并没有现成封装好的函数接口供程序调用。为了方便读者类比Socket编程来理解TDI编程,所以,接下来将基于TDI实现TCP客户端的实现原理分成5个部分来介绍,分别为:TDI初始化、TDI TCP连接、TDI TCP数据发送、TDI TCP数据接收以及TDI关闭。
    2.1 TDI初始化在调用TDI实现TCP数据通信之前,需要先初始化TDI操作。初始化操作主要包括创建本地地址对象、创建端点对象以及将端点对象与本地地址对象进行关联。那么,具体的TDI初始化实现步骤如下所示。
    首先,在创建本地地址对象之前,先构建本地地址拓展属性结构PFILE_FULL_EA_INFORMATION。主要设置该拓展属性结构的名称为TdiTransportAddress,拓展属性结构的内容是TA_IP_ADDRESS,里面存储着通信协议类型、本地IP地址及端口等信息。TA_IP_ADDRESS中的AddressType表示通信协议类型,TDI_ADDRESS_TYPE_IP则支持UDP和TCP等IP协议。将IP地址以及端口都置为0,表示本机本地IP地址和随机端口。
    在构建本地地址拓展属性结构完成之后,就可以调用ZwCreateFile函数来根据本地地址拓展属性结构创建本地地址对象。该函数中,打开的设备名称为“\\Device\\Tcp”,即打开TCP设备驱动服务。ZwCreateFile函数中的重要参数是拓展属性(Extended Attributes),通过拓展属性可以向其他的驱动程序传递信息。所以,驱动程序会将本地地址拓展属性结构的数据传递TCP设备驱动,创建本地地址对象,并获取对象句柄。在获取本地地址对象句柄后,通过调用ObReferenceObjectByHandle函数来获取本地地址对象的文件对象,并根据得到的文件对象调用IoGetRelatedDeviceObject获取对应的本地地址对象的驱动设备指针,以方便后续的操作。
    然后,便可以开始创建端点对象。同样的,在创建端点对象之前,先构建上下文拓展属性结构PFILE_FULL_EA_INFORMATION。主要设置该拓展属性结构的名称为TdiConnectionContext,拓展属性结构的内容为CONNECTION_CONTEXT。本文并没有用到CONNECTION_CONTEXT结构里的数据,所以都置为0。
    构建上下文拓展属性结构完成后,同样是调用ZwCreateFile函数根据上下文拓展属性结构来创建端点对象。仍是打开TCP设备驱动服务,向TCP设备驱动传递上下文结构数据,创建端点对象,并获取端点对象句柄。在获取端点对象句柄之后,直接调用ObReferenceObjectByHandle函数来获取端点对象的文件对象,以方便后续的操作。
    最后,创建了本地地址对象和端点对象后,需要将两者关联起来,没有关联地址的端点没有任何用处。其中,本地地址对象存储的信息向系统表明驱动程序使用的本地IP地址和端口。直接调用TdiBuildInternalDeviceControlIrp函数创建TDI的I/O请求包IRP,消息类型为TDI_ASSOCIATE_ADDRESS,表示端点对象关联本地地址对象;需要用到上述获取的本地地址驱动设备对象指针以及端点文件对象指针作为参数。TdiBuildInternalDeviceControlIrp实际是一个宏,它内部调用了IoBuildDeviceIoControlRequest,给这个宏的一些参数实际被忽略了。所以,再调用TdiBuildAssociateAddress宏,将上述获取的本地地址驱动设备对象指针以及端点文件对象指针添加到IRP的I/O堆栈空间中。
    完成上述3个操作之后,就可以调用IoCallDriver函数向驱动设备发送TDI的I/O请求包IRP。其中,驱动程序需要等待系统执行IRP,所以,需要调用IoSetCompletionRoutine设置完成回调函数,通知程序IRP执行完成。这样,TDI的初始化操作到此结束了。
    那么,TDI初始化的具体实现代码如下所示。
    // TDI初始化设置NTSTATUS TdiOpen(PDEVICE_OBJECT *ppTdiAddressDevObj, PFILE_OBJECT *ppTdiEndPointFileObject, HANDLE *phTdiAddress, HANDLE *phTdiEndPoint){ DbgPrint("Enter OpenTdi\n"); NTSTATUS status = STATUS_UNSUCCESSFUL; PFILE_FULL_EA_INFORMATION pAddressEaBuffer = NULL; ULONG ulAddressEaBufferLength = 0; PTA_IP_ADDRESS pTaIpAddr = NULL; UNICODE_STRING ustrTDIDevName; OBJECT_ATTRIBUTES ObjectAttributes = { 0 }; IO_STATUS_BLOCK iosb = { 0 }; HANDLE hTdiAddress = NULL; PFILE_OBJECT pTdiAddressFileObject = NULL; PDEVICE_OBJECT pTdiAddressDevObj = NULL; PFILE_FULL_EA_INFORMATION pContextEaBuffer = NULL; ULONG ulContextEaBufferLength = 0; HANDLE hTdiEndPoint = NULL; PFILE_OBJECT pTdiEndPointFileObject = NULL; KEVENT irpCompleteEvent = { 0 }; PIRP pIrp = NULL; do { // 为本地地址拓展属性结构申请内存及初始化 ulAddressEaBufferLength = sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TA_IP_ADDRESS); pAddressEaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool(NonPagedPool, ulAddressEaBufferLength); if (NULL == pAddressEaBuffer) { ShowError("ExAllocatePool[Address]", 0); break; } RtlZeroMemory(pAddressEaBuffer, ulAddressEaBufferLength); RtlCopyMemory(pAddressEaBuffer->EaName, TdiTransportAddress, (1 + TDI_TRANSPORT_ADDRESS_LENGTH)); pAddressEaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; pAddressEaBuffer->EaValueLength = sizeof(TA_IP_ADDRESS); // 初始化本机IP地址与端口 pTaIpAddr = (PTA_IP_ADDRESS)((PUCHAR)pAddressEaBuffer->EaName + pAddressEaBuffer->EaNameLength + 1); pTaIpAddr->TAAddressCount = 1; pTaIpAddr->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP; pTaIpAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP; pTaIpAddr->Address[0].Address[0].sin_port = 0; // 0表示本机任意随机端口 pTaIpAddr->Address[0].Address[0].in_addr = 0; // 0表示本机本地IP地址 RtlZeroMemory(pTaIpAddr->Address[0].Address[0].sin_zero, sizeof(pTaIpAddr->Address[0].Address[0].sin_zero)); // 创建TDI驱动设备字符串与初始化设备对象 RtlInitUnicodeString(&ustrTDIDevName, COMM_TCP_DEV_NAME); InitializeObjectAttributes(&ObjectAttributes, &ustrTDIDevName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); // 根据本地地址拓展属性结构创建本地地址对象 status = ZwCreateFile(&hTdiAddress, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, pAddressEaBuffer, ulAddressEaBufferLength); if (!NT_SUCCESS(status)) { ShowError("ZwCreateFile[Address]", status); break; } // 根据本地地址对象句柄获取对应的本地地址文件对象 status = ObReferenceObjectByHandle(hTdiAddress, FILE_ANY_ACCESS, 0, KernelMode, &pTdiAddressFileObject, NULL); if (!NT_SUCCESS(status)) { ShowError("ObReferenceObjectHandle[EndPoint]", status); break; } // 获取本地地址文件对象对应的驱动设备 pTdiAddressDevObj = IoGetRelatedDeviceObject(pTdiAddressFileObject); if (NULL == pTdiAddressDevObj) { ShowError("IoGetRelatedDeviceObject", 0); } // 为上下文拓展属性申请内存并初始化 ulContextEaBufferLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + TDI_CONNECTION_CONTEXT_LENGTH + 1 + sizeof(CONNECTION_CONTEXT); pContextEaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool(NonPagedPool, ulContextEaBufferLength); if (NULL == pContextEaBuffer) { ShowError("ExAllocatePool[EndPoint]", 0); break; } RtlZeroMemory(pContextEaBuffer, ulContextEaBufferLength); RtlCopyMemory(pContextEaBuffer->EaName, TdiConnectionContext, (1 + TDI_CONNECTION_CONTEXT_LENGTH)); pContextEaBuffer->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; pContextEaBuffer->EaValueLength = sizeof(CONNECTION_CONTEXT); // 根据上下文创建TDI端点对象 status = ZwCreateFile(&hTdiEndPoint, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, pContextEaBuffer, ulContextEaBufferLength); if (!NT_SUCCESS(status)) { ShowError("ZwCreateFile[EndPoint]", status); break; } // 根据TDI端点对象句柄获取对应的端点文件对象 status = ObReferenceObjectByHandle(hTdiEndPoint, FILE_ANY_ACCESS, 0, KernelMode, &pTdiEndPointFileObject, NULL); if (!NT_SUCCESS(status)) { ShowError("ObReferenceObjectHandle[EndPoint]", status); break; } // 设置事件 KeInitializeEvent(&irpCompleteEvent, NotificationEvent, FALSE); // 将TDI端点与本地地址对象关联, 创建TDI的I/O请求包:TDI_ASSOCIATE_ADDRESS pIrp = TdiBuildInternalDeviceControlIrp(TDI_ASSOCIATE_ADDRESS, pTdiAddressDevObj, pTdiEndPointFileObject, &irpCompleteEvent, &iosb); if (NULL == pIrp) { ShowError("TdiBuildInternalDeviceControlIrp", 0); return STATUS_INSUFFICIENT_RESOURCES; } // 拓展I/O请求包 TdiBuildAssociateAddress(pIrp, pTdiAddressDevObj, pTdiEndPointFileObject, NULL, NULL, hTdiAddress); // 设置完成实例的回调函数 IoSetCompletionRoutine(pIrp, TdiCompletionRoutine, &irpCompleteEvent, TRUE, TRUE, TRUE); // 发送I/O请求包并等待执行 status = IoCallDriver(pTdiAddressDevObj, pIrp); if (STATUS_PENDING == status) { KeWaitForSingleObject(&irpCompleteEvent, Executive, KernelMode, FALSE, NULL); } // 返回数据 *ppTdiAddressDevObj = pTdiAddressDevObj; *ppTdiEndPointFileObject = pTdiEndPointFileObject; *phTdiAddress = hTdiAddress; *phTdiEndPoint = hTdiEndPoint; }while (FALSE); // 释放内存 if (pTdiAddressFileObject) { ObDereferenceObject(pTdiAddressFileObject); // 测试 } if (pContextEaBuffer) { ExFreePool(pContextEaBuffer); } if (pAddressEaBuffer) { ExFreePool(pAddressEaBuffer); } DbgPrint("Leave OpenTdi\n"); return status;}
    2.2 TDI TCP连接在TDI初始化完成之后,驱动程序便可以向TCP服务端程序发送连接请求,并建立TCP连接。主要操作就是构造一个包含服务器IP地址以及监听端口的IRP,再发送给驱动程序执行。那么,基于TDI的TCP连接的具体实现流程如下所示。
    首先,直接调用TdiBuildInternalDeviceControlIrp宏创建IRP,设置IRP的消息类型为TDI_CONNECT,表示建立TCP连接。
    然后,构建TDI连接信息结构TDI_CONNECTION_INFORMATION,主要设置IP地址的相关信息TA_IP_ADDRESS,包括通信协议类型、服务器的IP地址以及服务器监听端口号等。并调用TdiBuildConnect宏将TDI连接信息结构的数据添加到IRP的I/O堆栈空间中。
    最后,调用IoCallDriver函数向驱动程序发送上述构建好的IRP,并创建完成回调函数等待系统处理IRP。系统成功处理完毕后,驱动程序便与服务端程序成功建立TCP连接。
    那么,TCP连接的具体实现代码如下所示。
    // TDI TCP连接服务器NTSTATUS TdiConnection(PDEVICE_OBJECT pTdiAddressDevObj, PFILE_OBJECT pTdiEndPointFileObject, LONG *pServerIp, LONG lServerPort){ DbgPrint("Enter TdiConnection\n"); NTSTATUS status = STATUS_SUCCESS; IO_STATUS_BLOCK iosb = { 0 }; PIRP pIrp = NULL; KEVENT connEvent = { 0 }; TA_IP_ADDRESS serverTaIpAddr = { 0 }; ULONG serverIpAddr = 0; USHORT serverPort = 0; TDI_CONNECTION_INFORMATION serverConnection = { 0 }; // 创建连接事件 KeInitializeEvent(&connEvent, NotificationEvent, FALSE); // 创建TDI连接I/O请求包:TDI_CONNECT pIrp = TdiBuildInternalDeviceControlIrp(TDI_CONNECT, pTdiAddressDevObj, pTdiEndPointFileObject, &connEvent, &iosb); if (NULL == pIrp) { ShowError("TdiBuildInternalDeviceControlIrp_TDI_CONNECT", 0); return STATUS_INSUFFICIENT_RESOURCES; } // 初始化服务器IP地址与端口 serverIpAddr = INETADDR(pServerIp[0], pServerIp[1], pServerIp[2], pServerIp[3]); serverPort = HTONS(lServerPort); serverTaIpAddr.TAAddressCount = 1; serverTaIpAddr.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP; serverTaIpAddr.Address[0].AddressType = TDI_ADDRESS_TYPE_IP; serverTaIpAddr.Address[0].Address[0].sin_port = serverPort; serverTaIpAddr.Address[0].Address[0].in_addr = serverIpAddr; serverConnection.UserDataLength = 0; serverConnection.UserData = 0; serverConnection.OptionsLength = 0; serverConnection.Options = 0; serverConnection.RemoteAddressLength = sizeof(TA_IP_ADDRESS); serverConnection.RemoteAddress = &serverTaIpAddr; // 把上述的地址与端口信息增加到I/O请求包中,增加连接信息 TdiBuildConnect(pIrp, pTdiAddressDevObj, pTdiEndPointFileObject, NULL, NULL, NULL, &serverConnection, 0); // 设置完成实例回调函数 IoSetCompletionRoutine(pIrp, TdiCompletionRoutine, &connEvent, TRUE, TRUE, TRUE); // 发送I/O请求包并等待执行 status = IoCallDriver(pTdiAddressDevObj, pIrp); if (STATUS_PENDING == status) { KeWaitForSingleObject(&connEvent, Executive, KernelMode, FALSE, NULL); } DbgPrint("Leave TdiConnection\n"); return status;}2.3 TDI TCP数据发送在成功建立TCP连接之后,客户端程序与服务端程序便可以相互通信,进行数据的交互了。基于TDI实现的TCP数据发送主要的操作便是构造一个发送数据的IRP,并向IRP添加发送的数据,然后发送给驱动程序处理即可。具体的实现流程如下所示。
    首先,直接调用TdiBuildInternalDeviceControlIrp宏创建IRP,设置IRP的消息类型为TDI_SEND,表示发送数据。
    然后,需要将发送的数据调用IoAllocateMdl函数来将数据创建一份新的映射并获取分配到的MDL结构,因为驱动程序接下来需要调用TdiBuildSend宏将TDI发送数据的MDL结构数据添加到IRP的I/O堆栈空间中,以此传递发送的数据信息。
    最后,调用IoCallDriver函数向驱动程序发送上述构建好的IRP,并创建完成回调函数等待系统处理IRP。处理完毕后,要记得调用IoFreeMdl函数来释放MDL。
    那么,TCP数据发送的具体实现代码如下所示。
    // TDI TCP发送信息NTSTATUS TdiSend(PDEVICE_OBJECT pTdiAddressDevObj, PFILE_OBJECT pTdiEndPointFileObject, PUCHAR pSendData, ULONG ulSendDataLength){ DbgPrint("Enter TdiSend\n"); NTSTATUS status = STATUS_SUCCESS; KEVENT sendEvent; PIRP pIrp = NULL; IO_STATUS_BLOCK iosb = {0}; PMDL pSendMdl = NULL; // 初始化事件 KeInitializeEvent(&sendEvent, NotificationEvent, FALSE); // 创建I/O请求包:TDI_SEND pIrp = TdiBuildInternalDeviceControlIrp(TDI_SEND, pTdiAddressDevObj, pTdiEndPointFileObject, &sendEvent, &iosb); if (NULL == pIrp) { ShowError("TdiBuildInternalDeviceControlIrp", 0); return STATUS_INSUFFICIENT_RESOURCES; } // 创建MDL pSendMdl = IoAllocateMdl(pSendData, ulSendDataLength, FALSE, FALSE, pIrp); if (NULL == pSendMdl) { ShowError("IoAllocateMdl", 0); return STATUS_INSUFFICIENT_RESOURCES; } MmProbeAndLockPages(pSendMdl, KernelMode, IoModifyAccess); // 拓展I/O请求包,添加发送信息 TdiBuildSend(pIrp, pTdiAddressDevObj, pTdiEndPointFileObject, NULL, NULL, pSendMdl, 0, ulSendDataLength); // 设置完成实例回调函数 IoSetCompletionRoutine(pIrp, TdiCompletionRoutine, &sendEvent, TRUE, TRUE, TRUE); // 发送I/O请求包并等待执行 status = IoCallDriver(pTdiAddressDevObj, pIrp); if (STATUS_PENDING == status) { KeWaitForSingleObject(&sendEvent, Executive, KernelMode, FALSE, NULL); } // 释放MDL if (pSendMdl) { IoFreeMdl(pSendMdl); } DbgPrint("Leave TdiSend\n"); return status;}
    2.4 TDI TCP数据接收基于TDI实现的TCP数据接收具体实现流程和数据发送类似,同样是构造数据接收的IRP,设置接收数据缓冲区,将IRP发送给驱动程序处理即可。具体的数据接收实现流程如下所示。
    首先,直接调用TdiBuildInternalDeviceControlIrp宏创建IRP,设置IRP的消息类型为TDI_RECV,表示发送接收。
    然后,需要将数据接收缓冲区调用IoAllocateMdl函数来将缓冲区创建一份新的映射并获取分配到的MDL结构,因为驱动程序接下来需要调用TdiBuildReceive宏将TDI接收数据缓冲区的MDL结构数据添加到IRP的I/O堆栈空间中,以此传递接收数据缓冲区的信息。
    最后,调用IoCallDriver函数向驱动程序发送上述构建好的IRP,并创建完成回调函数等待系统处理IRP。处理完毕后,要记得调用IoFreeMdl函数来释放MDL。
    那么,TCP数据接收的具体实现代码如下所示。
    // TDI TCP接收信息ULONG_PTR TdiRecv(PDEVICE_OBJECT pTdiAddressDevObj, PFILE_OBJECT pTdiEndPointFileObject, PUCHAR pRecvData, ULONG ulRecvDataLength){ DbgPrint("Enter TdiRecv\n"); NTSTATUS status = STATUS_SUCCESS; KEVENT recvEvent; PIRP pIrp = NULL; IO_STATUS_BLOCK iosb = { 0 }; PMDL pRecvMdl = NULL; ULONG_PTR ulRecvSize = 0; // 初始化事件 KeInitializeEvent(&recvEvent, NotificationEvent, FALSE); // 创建I/O请求包:TDI_SEND pIrp = TdiBuildInternalDeviceControlIrp(TDI_RECV, pTdiAddressDevObj, pTdiEndPointFileObject, &recvEvent, &iosb); if (NULL == pIrp) { ShowError("TdiBuildInternalDeviceControlIrp", 0); return STATUS_INSUFFICIENT_RESOURCES; } // 创建MDL pRecvMdl = IoAllocateMdl(pRecvData, ulRecvDataLength, FALSE, FALSE, pIrp); if (NULL == pRecvMdl) { ShowError("IoAllocateMdl", 0); return STATUS_INSUFFICIENT_RESOURCES; } MmProbeAndLockPages(pRecvMdl, KernelMode, IoModifyAccess); // 拓展I/O请求包,添加发送信息 TdiBuildReceive(pIrp, pTdiAddressDevObj, pTdiEndPointFileObject, NULL, NULL, pRecvMdl, TDI_RECEIVE_NORMAL, ulRecvDataLength); // 设置完成实例回调函数 IoSetCompletionRoutine(pIrp, TdiCompletionRoutine, &recvEvent, TRUE, TRUE, TRUE); // 发送I/O请求包并等待执行 status = IoCallDriver(pTdiAddressDevObj, pIrp); if (STATUS_PENDING == status) { KeWaitForSingleObject(&recvEvent, Executive, KernelMode, FALSE, NULL); } // 获取实际接收的数据大小 ulRecvSize = pIrp->IoStatus.Information; // 释放MDL if (pRecvMdl) { IoFreeMdl(pRecvMdl); } DbgPrint("Leave TdiRecv\n"); return status;}
    2.5 TDI关闭所谓的关闭TDI,主要是负责资源数据的释放和清理工作。通过调用ObDereferenceObject函数释放端点文件对象资源,调用ZwClose函数关闭端点对象句柄以及本地地址对象句柄。
    那么,TDI关闭的具体实现代码如下所示。
    // TDI关闭释放VOID TdiClose(PFILE_OBJECT pTdiEndPointFileObject, HANDLE hTdiAddress, HANDLE hTdiEndPoint){ DbgPrint("Enter TdiClose\n"); if (pTdiEndPointFileObject) { ObDereferenceObject(pTdiEndPointFileObject); } if (hTdiEndPoint) { ZwClose(hTdiEndPoint); } if (hTdiAddress) { ZwClose(hTdiAddress); } DbgPrint("Leave TdiClose\n");}
    3.测试在64位Windows 10操作系统上,先运行TCP服务端程序ChatServer.exe,设置服务端程序的IP地址以及监听端口分别为127.0.0.1和12345,并开始进行监听。然后,直接加载并运行上述驱动程序,连接监听状态的服务端程序,连接成功,并成功向服务端程序发送数据“I am Demon`Gan—->From TDI”。服务端程序成功与驱动程序建立TCP连接,并成功接收来自驱动程序发送的数据。处于用户层的服务端程序向驱动程序发送数据“nice to meet you, Demon”,驱动程序也能成功接收。所以,基于TDI通信的驱动程序测试成功,如图7-3所示。

    打开cmd.exe命令行窗口后,输入命令netstat -ano来查看网络连接情况以及对应的进程PID,执行命令的结果如图7-4所示,从中可以知道,与服务端程序建立通信连接的进程PID为4,即system.exe进程,因为是驱动程序与用户程序建立的TCP连接,所以,进程PID显示为4。

    4.小结基于TDI的TCP客户端的实现原理实际上是通过构造不同信息的TDI的I/O请求包IRP,携带不同的参数数据,发送给驱动函数进行处理实现的。实现该程序的关键,则是在于TDI的I/O请求包IRP的构建上。
    其中,在通信的过程中,要注意及时调用IoFreeMdl函数来释放创建的MDL。同时,驱动程序可以通过调用PsCreateSystemThread函数创建一个多线程,循环接收来自服务端程序的数据。
    0 留言 2021-08-30 11:08:21 奖励38点积分
  • 使用强度影像提升描述子在基于Segment的3D激光SLAM重定位中的作用

    On the descriptive power of LiDAR intensity images for segment-based loop closing in 3D SLAM摘要本文首次提出使用强度影像增强3D Segment(段)的描述子的描述能力,本文主要方法聚焦于学到一个具有描述占据部分影像的segments的特征,同时具备描述周围纹理信息的描述子。本文的方法是对Segmap框架的扩展,将一个新型的基于DNN的描述子插入到Segmap中(原始的Segmap中就有一个基于CNN的描述子…)。首先通过LiDAR点云帧做位姿估计和运动补偿,完成激光里程计,然后通过渐进式的欧式空间分割将点云帧分割为不同的块,这里保存了人造的强度影像信息和深度影像信息。回环候选是通过在目标地图中的描述子空间进行KNN搜索得到的,在通过一致性验证模块之后完成回环位姿的估计。为什么说是人造的强度影像和深度影像呢?是因为作者将3D LiDAR做了圆柱投影,投影成强度图像。通过多分支的网络将由体素格网作为输入的3D几何分支与2D图像组成的强度分支融合成一个64x1的特征描述子。

    利用LiDAR强度影像中获取的边界视觉纹理信息来增强3D Segment的描述子的描述能力。通过注意力热图可以看出本文提出的网络主要聚焦于暖色系标识区域的特征提取。
    方法概述本文的框架是对Segmap的扩展,主要修改了特征描述子提取,匹配和局部地图构建模块,其他部分均继承了Segmap的组件模块。
    网络结构图通过强度影像分支和3D体素几何分支学习segment的描述子。是一个3D网络和2D网络结合的网络架构,最后返回一个64x1的特征向量。将原始的点云转换为64x1维的描述子,点云地图表示为segment的特征描述子组成的特征地图,完成地图的压缩,做到大场景的重定位。相比于SegMatch中的描述子,本文的描述子效率还略有提升。
    1 留言 2021-09-14 13:14:37 奖励40点积分
  • 初学python之(A+B问题)

    题目描述:
    从键盘上输入两个整数,并输出这两个数的和,即你的任务是计算 a+b。
    输入:
    输入空格分隔的两个整数
    输出:
    对于用空格分隔的两个整数,求其和。
    #by:xiamo 2021-06-28 09:55 zunyiprint(sum(map(int, input().split())))
    测试效果:
    3 留言 2021-06-28 10:30:51 奖励30点积分
  • 基于深度学习的三维场景几何约束相机定位

    3D Scene Geometry-Aware Constraint for Camera Localization with Deep Learning摘要摄像机定位是自动驾驶车辆和移动机器人全局定位的基础和关键组成部分,可用于进一步的环境感知、路径规划和运动控制。近年来,基于卷积神经网络的端到端方法被大量研究,以实现甚至超越基于三维几何的传统方法。在这项工作中,我们提出了一个紧凑的网络绝对相机位姿回归。从这些传统的方法中得到启发,利用所有可用的信息,包括运动、深度和图像内容,引入了三维场景几何感知约束。通过定义像素级光度损失和相机结构相似性损失,我们将这个约束作为正则化术语添加到我们提出的网络中。为了衡量我们的方法,我们用我们提出的方法和最新的技术在具有挑战性的不同场景,包括室内和室外环境中进行了测试。实验结果表明,该方法在预测精度和收敛效率方面都有显著提高。
    算法我们提出的3D场景几何感知约束下的摄像机定位的自监督深度学习的示意图表示。

    Microsoft 7-Scene上的摄像头定位结果。从左到右,四个测试序列分别是头-01序列、火-04序列、南瓜-01序列、楼梯-01序列。这三个结果从上到下分别来自PoseNet[2]、MapNet[24]和我们的方法(绿色表示ground truth,红色表示prediction)。

    牛津机器人汽车数据集原始彩色图像和深度图由激光雷达捕获。环路路由子集,总长度为1120m。
    2 留言 2021-07-15 07:55:10 奖励42点积分
  • 【吐槽】WRITE-BUG

    来write-bug几天了,感觉好冷清啊,好多帖子都是以前的,萌新一枚,请问有人么?🤪🤨🧐
    5 留言 2021-08-12 11:51:05 奖励100点积分
  • 基于JSP和SQL Server 2014实现的飞机售票管理系统

    一、系统需求分析1.1 引言随着近年来,旅游热潮逐渐升温,人们经常利用节假日,进行旅行。这时航空公司对自身的销售系统进行提升,开发“机票预订系统”对自身的销售过程有相当大的帮助,所以有必要开发高效的机票预订系统。“机票预订系统”的主要功能是帮助航空公司对乘客的信息进行统计、更新。并对自己的航班做出相应的调整,来达到最好的运输效果。
    其功能描述如下:

    乘客访问本系统,查询航班和机票,最后预订工作人员看到预订信息后与乘客联系,经确定后等待乘客支付乘客付款后,系统印出取票通知和帐单给乘客或提供送票上门服务航空公司为随时掌握各航向飞机的乘载情况,系统定期进行查询统计,以便适当调整系统对旅客延误了取票时间的处理系统对班机取消后的处理乘客临时更改机票班次的处理
    1.2 工作流程本系统工作流程为:系统启动并进入登录界面,根据不同的用户进行登录,所有用户都可以浏览系统主页面。通过权限判断,普通用户只能浏览、阅读和查询信息,注册用户除了可以完成普通用户的操作外,还可以进行对自己注册资料的操作(包括对自己的资料的录入、修改和删除的操作)。管理员可以对系统信息进行添加、修改和删除的权限,及对系统管理的所有功能进行操作。所有用户均可以实现查询功能。
    本系统是将现代化的计算机技术与管理服务模式相结合,按照销售管理的服务流程设计完成的。同时为扩展服务范围,初步设计一个销售平台以利于信息发布和管理。为了使系统在各项管理中发挥更大的作用,实现工作过程的计算机化,提高工作效率和工作质量,现提出如下系统开发目标:

    操作简单、界面友好:完全控件式的页面布局,使得信息的录入工作更简便;许多选项包括信息类别、来源、作者等只需要点击鼠标就可以完成;另外,跟踪出现的提示信息也让用户随时清楚自己的操作情况
    即时可见:对信息的处理(包括添加、修改、删除)将立即在主页的对应栏目显示出来,达到”即时发布、即时见效”的功能
    功能完善:不仅包括常见系统的信息管理的各个方面:信息录入、浏览、删除、修改、查询等各个方面,完整地实现了系统对即时信息的管理要求。而且,还能进行对会员的管理,对会员的资料进行审核,以及上传文件的管理。同时,为了能有效方面的更新系统的界面
    方便移植:系统应具有实用性、可靠性和适用性,同时注意到先进性。针对不同的用户,只需要稍作修改就可以开发出适合自己特点的网上书店。系统界面中所需的条目,图片等数据都存放于数据文件中,只要对该文件做部分修改,就能在系统界面上实现及时更新的效果,减少了更改系统源代码的复杂性
    动态管理:对系统数据库实行动态操作,能实现对数据库信息的动态查询、动态更新修改和动态录入数据

    1.3 可行性分析
    要求:

    能自动的预订出乘客的机票,安排好航班输出如报告、文件或数据:取票通知和账单
    目标:

    人力与设备费用的减少处理速度的提高管理信息服务的改进自动决策系统的改进人员利用率的改进

    使用现有的技术能不能实现这个系统。由于本管理系统的对象单一,仅对航空公司机票进行管理。因此,这个管理系统比较适合采用数据库来设计。在计算机硬件和软件快速发展的今天,计算机硬件和软件已经远远满足本管理系统的要求。在数据库编程工具方面,各种可视化编程方法的出现,一改过去程序设计的概念和方法,用户用鼠标就可以快速、简捷地创建应用程序,极大地提高了编程效率。
    1.4 软件设计需求分析由 JSP 开发的机票预订系统具有界面友好,操作简单,使用方便的特点。在系统运作过程中,任何计算机专业与非专业人员都能轻松使用和管理。本系统是以 SQL Server 2014 的企业版作为数据库设计平台。SQL Server 2014 的企业管理器是图形化的集成管理工具,功能强大,而且使用比较简单。可以说,本系统在操作上是可行的。
    开发工具

    服务器端操作系统: Windows 10客户端操作系统:Windows 10开发工具: Myeclipse数据库:SQL Server2014
    二、系统设计2.1 总体设计2.1.1 系统设计总目标在计算机网络,数据库和先进的开发平台上,利用现有的软件,配置一定的硬件,开发一个具有开放体系结构的、易扩充的、易维护的、具有良好人机交互界面的机票预定系统,实现航空公司的机票销售的自动化的计算机系统,该系统全面、正确、完整、及时地收集、加工、整理、清算在整个订票业务流程中所发生的各类资金、订单、机票等有关信息。
    2.1.2 系统设计功能
    条线贯穿着客户注册、查询、订票、更改、取消、留言、帮助等
    条线管理着信息更新、资金帐户、机票、结算等,并进行监视分析

    2.1.3 系统设计内容和要求根据航空公司的需要,系统应当包含基本的功能有:用户注册、用户登录和管理员登录,航班查看等。用户则划分为基本乘客和管理员两大类,管理员还兼具有管理职能。功能模块图如下:

    2.1.4 输入输出环节出设计是系统设计中很容易被忽视的环节,又是一个重要的环节。一个好的输出系统设计可以为用户和系统双方带来良好的工作环境,一个好的输出设计可以为管理者提供简捷、明了、有效、实用的管理和控制信息。本管理系统输入设计数据根据输出的要求来选择。输出类型设计是根据需求设计为打印输出,这是用户通常要求的最主要的形式。输入内容设计主要是根据数据库设计和输出设计的结果来确定的。外部输入主要是键盘输入,内部输入只要是数据库中调用的信息,如会员资料信息的注册,在后台对帖子管理,用户信息,重要通知的添加。5.代码设计
    设计代码的目的是为了便于计算机和人识别和处理,更方便的进行分类和排序,使计算机对数据进行快速的查询。对客观实体,通过代码进行识别、分类。从计算机信息处理而言,使用代码节省处理时间与空间,提高处理速度与效率。代码设计在系统中,它是人与计算机的共同语言,起着沟通人与计算的作用。采用代码,可以使数据表达标准化、简化程序设计、加快输入、减少出错率、节省存储空间、提高处理速度。代码设计是一项重要的基础工作。因此,代码设计有他特有的意义。
    2.2 系统详细设计2.2.1 数据库整体设计(E-R 图)数据库需求分析定义了数据本身的静态特征,但他们反映的仅是应用需求,并无反映出数据之间的本质联系。管理系统主要以管理员和用户来共同完成一个业务流程:

    2.2.2 航班实体图、机票实体图及简单说明之间的关系。机票实体图,如图 4-4 示,机票实体与航班实体是多对一的关系。

    航班实体图,如图 3-3 所示,航班实体与机票实体为一对多关系。

    2.2.3 数据库物理设计本系统用到的数据表有:管理员表、航班表、机票表等。下面将对各个数据库表做介绍。
    管理员表(ADMIN)
    管理员表如表 4-1 所示,其中 ID 字段为主键且自动增长,此表只保存系统管理员的用户名和密码。



    字段名称
    字段类型
    字段长度
    字段说明




    ID
    int
    4
    唯一编号,主键


    ADMIN
    varchar
    15
    用户名


    PASSWORD
    varchar
    50
    密码



    航班表(FLIGHT)
    航班表如表 5-5 所示,此表描述的是航班信息,其中 ID 字段为主键且自动增长,航班编号(FLIGHT_CODE)唯一。



    字段名称
    字段类型
    字段长度
    字段说明




    ID
    int
    4
    唯一编号,主键


    FLIGHT_CODE
    varchar
    50
    航班编号


    FLIGHT_END
    varchar
    50
    目的城市


    START_TIME
    varhcar
    50
    出发时间


    ARRIVE_TIME
    varchar
    50
    到达时间


    FLIGHT_DATE
    varchar
    50
    班期


    FLIGHT_COMPANY
    varchar
    50
    航空公司


    FLIGHT_PLANE
    varchar
    50
    机型


    FLIGHT_TYPE
    char
    1
    类别


    REMARK
    varchar
    100
    备注



    机票表(NEWS)
    机票表如表 5-7 所示,此表描述的是机票信息,其中 ID 字段为主键且自动增长,机票等级(GRADE)为 int 型,设 1 为经济仓,2 为头等仓。



    字段名称
    字段类型
    字段长度
    字段说明




    ID
    int
    4
    唯一编号,主键


    FLIGHT_CODE
    varchar
    50
    航班号


    START_DATE
    datetime
    8
    出发时间


    GRADE
    int
    4
    1 为经济仓;2 为头等仓


    NUMBER
    int
    4
    数量


    PRICE
    float
    8
    价格


    DISCOUNT
    float
    8
    折扣率


    REMARK
    varchar
    100
    备注



    2.3 系统模块化由于本系统所涉及到的信息较多,所以可以按不同功能划分成以下 4 个模块:管理模块,航班管理模块,航空公司管理模块,订票管理模块。
    设计系统功能模块
    随着 JSP 技术的成熟,越来越多的开发人员开始使用 Web 应用框架。框架是一由一些类组成,这些类为应用程序提供了一个可重用的设计,或者应用程序中的一层。应用程序代码访问类库从而执行任务,而框架是调用应用程序代码,从而管理程序的流程。框架为 Web 应用提供了预备的软件架构和相关的软件包,它大大提高了开发 Web 应用的速度和效率。
    2.4 连接数据库数据库建立连接、发送 SQL 语句并处理结果。下列代码段给出了以上三步的基本示例:
    Connection con = DriverManager.getConnection("jdbc:odbc:wombat", "login", "password");Statement stmt = con.createStatement();ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");while (rs.next()) { int x = rs.getInt("a"); String s = rs.getString("b"); float f = rs.getFloat("c");}
    2.5 设计登录及修改密码功能模块登录为管理员,登录成功后可进行相应的操作界面,登录模块可在客户端和服务器端进行验证,无需写代码,也不需要写 JavaScript 脚本即可验证用户名、密码是否为空,密码长度等。如图 3-7 所示。

    2.6 设计航班管理功能模块航班管理同样也分为航班的增删改查,在/admin/文件夹下由 FlightAction.java、flightForm.java、FlightDAO.java、FlightDTO.java 实现,前台 JSP 页面有 flightManage.jsp、flightAdd.jsp、flightUpdate.jsp,添加航班时机型为数据库存在的机型,只能在下拉框中选择,如图 3-8 所示。


    2.7 设计航空公司管理功能模块航空公司管理由在/ WEROOT /文件夹下 TicketAction.java、ticketForm.java、TicketDAO.java、TicketDTO.java 实现,前台 JSP 页面有 ticketManage.jsp、ticketAdd.jsp、ticketUpdate.jsp、ticketQuery.jsp, 添加机票时航班为数据库存在的航班,只能在下拉框中选择,如图 3-9 所示。

    航空公司管理实现机票的修改和删除,显示机票对应的航班某些信息及自身信息,具体如图 3-11 所示。

    2.8 设计订票管理功能模块订票管理分为订票信息的删除、修改、查询、增加,在/WEROOT/文件夹下文件路径 BespeakAction.java、bespeakForm.java、BespeakDAO.java、BespeakDTO.java,前台 JSP 页面有 bespeakManage.jsp、bespeakAdd.jsp、bespeakUpdate.jsp、bespeakQuery.jsp, 添加订票信息需要输入相关订票者信息,而不需要添加任何机票信息,系统会自动添加乘客选中的机票相关信息。为了防止恶意订票及时下网上存在的一些注册机进行批量注册,为此添加了注册码功能,如图 6-6 所示。
    订票管理中增加了购买功能,乘客预订机票后,预订信息中显示的状态为预订,当乘客付款领取机票时,操作员会点击相应记录后的“购买”,然后订票信息状态发生改变,由预订变为购买,同时机票表中对应机票数量相应减少。如图 3-12 所示。

    三、系统实现3.1 系统测试的目的
    软件测试是为了发现错误而执行程序的过程测试是为了证明程序有错,而不是证明程序无错误一个好的测试用例是在于它能发现至今未发现的错误成功的测试是发现了至今未发现的错误的测试
    3.2 系统测试的具体实现3.2.1 登陆界面测试进入登陆界面,输入错误的户名和密码,点击登陆,测试系统是否弹出错误提示,提示输入的用户名和密码错误。输入正确的普通用户名和密码点击登陆查看是否进入普通用户界面,输入管理员用户名和密码点击登陆查看是否进入管理员界面。点击退出系统按钮,是否弹出确认退出的对话框,点击“是”退出系统,点击“否”不作任何操作。发现问题,密码输入没有经过加密导致用户密码可见,通过对输入框属性的设定将密码加密,再次运行时输入密码显示为*号。
    3.2.2 信息查询功能测试进入查询界面,在区、镇、村的下拉列表中选择不同的区、镇、村和企业点击查看,看是否调出对应的信息。点击“退出”按钮,看是否有对话框弹出,提示是否退出系统。
    在关键字查询对话框中输入查询信息的关键字,点击查询,测试系统是否显示相关的信息。
    3.2.3 数据维护功能测试进入数据维护界面,在选择需要维护数据的下拉列表框中选择要维护的表,测试系统是否显示对应的表格。选定表后在关键字查询中键入关键字,点击查询,测试系统是否成功在数据库表中检索到与关键字对应的信息。
    点击“插入”测试是否在表格中出现新的可编辑行。编辑数据后点击保存,退出系统,再进入系统测试数据是否真的被保存
    选中一条数据点击删除当前行,点击保存,退出系统,再进入系统后测试信息是否被删除
    为了测试系统对于误操作的反应,点击全部删除后不点保存,直接退出系统。再次进入系统测试数据是否删除。为了保证系统操作的安全可靠,不点击保存操作的结果就不会被存入数据库。
    3.2.4 用户信息维护功能测试用户信息维护界面拥有与数据维护界面相同的插入、删除、清空功能,对这些功能的测试步骤同数据维护功能测试。对用户信息维护测试的关键在于权限的设定是否起作用。在用户表中添加两个用户信息分别将权限设定为普通用户和管理员,保存后在系统登陆界面分别以新的用户名登陆,测试系统是否将新用户信息存入数据库,并且能在登陆时根据新的用户信息登陆到对应的界面。
    3.2.5 软件测试的结论软件基本达到设计要求,软件功能完整,用户界面良好,错误处理正确,且能正确提示错误种类。
    但是在测试中也发现软件的一些不足与缺陷, 如在数据维护界面中,当退出系统时系统只能提示是否退出系统,不能在未保存操作的情况下先提示用户保存操作再退出系统,所以,需要在此基础上进一步的予以纠正。
    3.3 系统各项运行结果3.3.1 首页效果图
    关键代码如下:详见 index.jsp
    <html> <head></head> <body text="#000000" bgcolor="#ffffff" leftmargin="0" topmargin="0"> <script language="JavaScript"></script> <script type="text/javascript"> function ss(){ if(document.form1.stime.value==""||document.form1.etime.value=="") { alert("请选择起飞时间和抵达时间!"); return false; }}</script> <form action="<%=basePath %>sinfo.jsp" method="post" name="form1" onsubmit="return ss()"> <table class="dragTable" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="head" colspan="2"> <span class="TAG">航班信息查询</span> <span class="more" style="FLOAT: right"></span> </td> </tr> <tr> <td width="40%" align="right">飞行航线</td> <td align="left"><select name="splace"> <%List hblist=hk.getALLSPLACE(); for(int i=0; i <option value="<%=hblist.get(i).toString() %>"><%=hblist.get(i).toString() %></option> <% } %> </select></td> </tr> <tr> <td width="40%" align="right">起飞时间</td> <td align="left"><input class="inputb" maxlength="24" size="30" name="stime" readonly="" onclick="SelectDate(this,'yyyy-MM-dd')" this.txt_date.attributes["onclick"]="SelectDate(this,'yyyy-MM-dd')" ;="" /> </td> </tr> <tr> <td width="40%" align="right">抵达时间</td> <td align="left"><input class="inputb" maxlength="24" size="30" name="etime" readonly="" onclick="SelectDate(this,'yyyy-MM-dd')" this.txt_date.attributes["onclick"]="SelectDate(this,'yyyy-MM-dd')" ;="" /> </td> </tr> <tr> <td width="40%" align="right">票价类别</td> <td align="left"><select name="passtype"> <option value="price">成人票价</option> <option value="cprice">儿童票价</option> <option value="mprice">会员票价</option> </select></td> </tr> <tr> <td colspan="2" align="center"><input type="submit" value="查询航班信息" /></td> </tr> </tbody> </table> </form> <table class="dragTable" cellspacing="0" cellpadding="0" width="100%" align="center" border="0"> <tbody> <tr> <td> </td> </tr> </tbody> </table> <script language="JavaScript"> </script> </body></html>
    3.3.2 国内航班效果图
    关键代码如下:详见 guonei.jsp
    <html> <head></head> <body> <table class="dragTable" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td class="head" colspan="2"> <span class="TAG">国内航班</span> </td> </tr> <tr> <td class="middle" align="left"> <alljoblist.size(); alln1++)="" {="" list="" alljob2="(List)AlljobList.get(alln1);" int="" j="hk.getSUM(alljob2.get(0).toString());" m="hk.getSUMPRICE(alljob2.get(0).toString());" n="hk.getSUMMPRICE(alljob2.get(0).toString());" o="hk.getSUMPRICE(alljob2.get(0).toString());" %=""> </alljoblist.size();> <table class="xsnr" id="tb1_nr1" cellspacing="0" cellpadding="0" width="100%" border="1"> <tbody> <tr> <td width="5%" align="center">序号</td> <td align="center">类型</td> <td align="center">航线</td> <td align="center">起飞时间</td> <td align="center">到达时间</td> <td align="center">航空公司</td> <td align="center">成人票价</td> <td align="center">儿童票价</td> <td align="center">会员价格</td> <td align="center">折率</td> <td align="center">余票</td> <td align="center">预定</td> </tr> <% List AlljobList=hk.getALLHB("国内航线"); if(!AlljobList.isEmpty()) { for(int alln1=0; alln1 <tr class="trA" onmouseover="this.className='trB'" onmouseout="this.className='trA'"> <td width="5%" align="center" style="border-bottom:1px dotted #ccc;"><%=alln1+1 %></td> </tr> </tbody> </table></td> </tr> </tbody> </table> </body></html>
    3.3.3 用户注册个人信息效果图
    关键代码如下:详见 personreg.jsp
    <html> <head></head> <body> <table class="dragTable" height="75" cellspacing="0" cellpadding="0" width="100%" align="center" border="0"> <tbody> <tr> <td class="head"> <font color="#000000">您现在的位置: 会员申请>> > 详细资料</font> </td> </tr> <tr> <td valign="top" align="middle" bgcolor="#f5f5f5"><br /> <form id="regform" name="regform" action="<%=basePath%>Member.shtml?method=PREG" method="post"> <table cellspacing="0" cellpadding="0" width="100%" bgcolor="#f5f5f5" border="0"> <tbody> <tr> <td align="left" class="white" width="100%" bgcolor="#c8c8c8" height="23">  <span class="black"><font color="blue"><strong>用户注册个人信息: </strong></font>           <font color="#FF0000">**</font>为必填写字段    </span></td> </tr> <tr> <td valign="top" align="middle" bgcolor="#ffffff" height="209"> <div align="center"> <table height="237" cellspacing="0" cellpadding="0" width="100%" border="0" hspace="12"> <tbody> <tr e=""> <td valign="center" align="right" height="12">用 户 名:</td> <td width="556" height="24" align="left"><input class="inputb" maxlength="24" size="30" name="username" value="<%=request.getAttribute(" username")="" %="" />" readonly></td> </tr> <tr> <td valign="center" align="right" height="24">登陆密码:</td> <td width="556" height="24" align="left"><input class="inputb" maxlength="24" size="30" name="password" value="<%=request.getAttribute("password") %>" readonly="" /></td> </tr> <tr> <td valign="center" align="right" height="26">    真实姓名:</td> <td width="556" height="26" align="left"> <input class="inputb" maxlength="24" size="30" name="realname" /> <font color="#ff0000">**</font> </td> </tr> <tr> <td valign="center" align="right" height="26">    性        别:</td> <td width="556" height="26" align="left"> <input type="radio" name="sex" value="m" checked="checked" /> 男 <input type="radio" name="sex" value="f" />女</td> </tr> <tr> <td valign="center" align="right" height="26">    出生日期:</td> <td width="556" height="26" align="left"> <input class="inputb" maxlength="24" size="30" name="bir" readonly="" onclick="SelectDate(this,'yyyy-MM-dd')" this.txt_date.attributes["onclick"]="SelectDate(this,'yyyy-MM-dd')" ;="" /> 格式:2008-01-01</td> </tr> <tr> <td valign="center" align="right" height="12">籍        贯:</td> <td height="12" align="left"> <select style="WIDTH: 80px" name="sheng" id="sheng" onchange="ProvinceChange(this, regform.city);"> <option selected="selected" <select="" style="WIDTH: 80px" name="city" id="city"> </option><option selected="selected" value="">-请选择-</option> </select> <font color="#ff0000">**</font> </td> </tr> <tr> <td valign="center" align="right" height="12">联系电话:</td> <td height="12" align="left"> <input class="inputb" name="telphone" maxlength="18" size="30" onkeyup="this.value=this.value.replace(/\D/gi,'')" /> <font color="#ff0000">**</font> </td> </tr> <tr> <td valign="center" align="right" height="12">E-mail:</td> <td height="12" align="left"> <input class="inputb" name="email" maxlength="50" size="30" /> <font color="#ff0000">**</font> <font color="#ff6600">(此电子邮件非常重要,请认真填写!)</font></td> </tr> </tbody> </table> </div></td> </tr> </tbody> </table> </form></td> </tr> </tbody> </table> </body></html>
    3.3.4 注册成功效果图
    核心代码如下:详见 login.jsp
    <html> <head></head> <body> <table class="MainTable" cellspacing="0" cellpadding="0" width="100%" align="center" border="0"> <tbody> <tr> <td> <table class="dragTable" id="viewarticle" cellspacing="0" cellpadding="0" width="100%" align="center" border="0"> <tbody> <tr> <td class="head"> <h3 class="L"></h3><font color="#000000">您现在的位置:会员登录>> > 找回密码</font> </td> </tr> </tbody> </table> <script type="text/javascript"> function lostpwd(){ if(document.form1.username.value=="") { alert("请输入用户名!"); document.form1.username.focus(); return false; } if(document.form1.question.value=="") { alert("请输入密码提示问题!"); document.form1.question.focus(); return false; } if(document.form1.answer.value=="") { alert("请输入提示问题答案!"); document.form1.answer.focus(); return false; } document.form1.method.value="lostpwd"; form1.submit();}</script> <div align="center"> <table class="dragTable" cellspacing="0" cellpadding="0" width="100%" align="center" border="0"> <tbody> <tr> <td width="100%" align="left" bgcolor="#c8c8c8" height="28">  <span class="black"><strong><font color="blue">请仔细填写以下资料:</font></strong></span></td> </tr> <tr> <td> <form name="form1" action="<%=basePath%>Member.shtml" method="post"> <input type="hidden" name="method" /> <table height="170" cellspacing="0" cellpadding="0" width="100%" border="0" hspace="12"> <tbody> <input type="hidden" name="method" /> <tr> <td align="right" width="38%" height="30">用 户 名: </td> <td width="62%" height="30" align="left"> <input class="inputb" size="22" name="username" /> </td> </tr> <tr class="black"> <td align="right" width="38%" height="30">提示问题: </td> <td width="62%" height="30" align="left"> <input class="inputb" size="22" name="question" /> </td> </tr> <tr class="black"> <td align="right" width="38%" height="30">问题答案: <input type="hidden" name="reg_type" value="person" /></td> <td width="62%" height="30" align="left"> <input class="inputb" size="22" name="answer" /> </td> </tr> <tr class="black"> <td align="middle" colspan="2" height="40"> <img src="<%=basePath" %="" />images/sub.gif onclick="lostpwd()" border=0>    <img src="<%=basePath" %="" />images/back.gif onclick="window.location='<%=basePath %>'" border=0> </td> </tr> </tbody> </table> </form> </td> </tr> </tbody> </table>  </div> <%@ include file="iframe/foot.jsp"%></td> </tr> </tbody> </table> </body></html>
    四、总结经过精心设计,系统基本达到了前期所提出的要求,完成了对用户登录模块、主模块、用户信息管理模块、新闻管理模块、航班管理模块、机型管理模块、机票管理模块及订票管理模块的设计,实现了对各个信息的添加、查询、修改和删除等功能,能够满足用户对各个信息进行管理的目标,在界面设计上达到了简单、清晰、友好的目标。
    在设计过程中也遇到了不少困难。在对用户需求进行分析阶段,对用户的需求没有掌握好,导致设计和建设数据库有些不合理,发现时很多地方需要修改甚至重做。由此才真正体会对用户需求分析是软件设计过程中不可缺少的部分,而且如果没有充分了解用户的需求时设计系统,最后将会导致软件不能满足用户的需求,甚至可以说。虽然说系统已经基本达到要求,但是由于时间和技术水平问题,还有些问题没有完善和解决,特别是添加新闻时没有嵌入编辑器,这是日后需要完善的地方。
    1 留言 2021-07-01 21:50:54 奖励70点积分
  • 创建计划任务实现开机自启动 精华

    背景想必实现程序开机自启动,是很常见的功能了。无论是恶意程序,还是正常的应用软件,都会提供这个功能,方便用户的使用。程序开机自启动,顾名思义,就是计算机开机后,不用人为地去运行程序,程序就可以自己运行起来。对于这个功能的,一直都是杀软重点监测的地方。因为,对于病毒来说,重要的不是如何被破坏,而是如何启动。
    在过去写的大大小小的程序中,我也实现过程序自启动的功能。现在,我把这些自启动功能的实现方式进行下总结。常见的方式有:修改开机自启动注册表、开机自启动目录、创建开机自启计划任务、创建开机自启系统服务等方式。现在对这些技术一一进行分析,并形成文档分享给大家。本文介绍的是创建自启动任务计划实现开机自启动的方式,其它的实现方式可以搜索我写的相关系列的文档。
    实现原理我是使用Windows Shell编程实现创建任务计划,所以会涉及COM组件相关知识。
    现在,为方便大家的理解,我把整个程序的逻辑概括为 3 各部分,分别是:初始化操作、创建任务计划以及删除任务计划。现在,我们一一对每一个部分进行解析。
    初始化操作初始化操作的目的就是获取 ITaskService 对象以及 ITaskFolder 对象,我们创建任务计划,主要是对这两个指针进行操作。具体流程是:

    首先初始化COM接口环境,因为我们接下来会使用到COM组件
    然后,创建 ITaskService 对象,并连接到任务服务上
    接着,从 ITaskService 对象中获取 ITaskFolder 对象

    这样,初始化操作就完成了,接下来就是直接使用 ITaskService 对象以及 ITaskFolder 对象进行操作了。
    创建任务计划现在,解析下任务计划的具体创建过程:

    首先,从 ITaskService 对象中创建一个任务定义对象 ITaskDefinition,用来来创建任务
    接着,就是开始对任务定义对象 ITaskDefinition进行设置:

    设置注册信息,包括设置作者的信息
    设置主体信息,包括登陆类型、运行权限
    设置设置信息,包括设置在使用电池运行时是否停止、在使用电池是是否允许运行、是否允许手动运行、是否设置多个实例
    设置操作信息,包括启动程序,并设置运行程序的路径和参数
    设置触发器信息,包括用户登录时触发

    最后,使用 ITaskFolder 对象根据任务定义对象 ITaskDefinition的设置,注册任务计划

    这样,任务计划创建的操作就完成了,只要满足设置的触发条件,那么就会启动指定程序。
    删除任务计划 ITaskFolder 对象存储着已经注册成功的任务计划的信息,我们只需要将任务计划的名称传入其中,调用DeleteTask接口函数,就可以删除指定的任务计划了。
    编码实现创建任务计划的初始化CMyTaskSchedule::CMyTaskSchedule(void){ m_lpITS = NULL; m_lpRootFolder = NULL; // 初始化COM HRESULT hr = ::CoInitialize(NULL); if(FAILED(hr)) { ShowError("CoInitialize", hr); } // 创建一个任务服务(Task Service)实例 hr = ::CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID *)(&m_lpITS)); if(FAILED(hr)) { ShowError("CoCreateInstance", hr); } // 连接到任务服务(Task Service) hr = m_lpITS->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()); if(FAILED(hr)) { ShowError("ITaskService::Connect", hr); } // 获取Root Task Folder的指针,这个指针指向的是新注册的任务 hr = m_lpITS->GetFolder(_bstr_t("\\"), &m_lpRootFolder); if(FAILED(hr)) { ShowError("ITaskService::GetFolder", hr); }}
    创建任务计划BOOL CMyTaskSchedule::NewTask(char *lpszTaskName, char *lpszProgramPath, char *lpszParameters, char *lpszAuthor){ if(NULL == m_lpRootFolder) { return FALSE; } // 如果存在相同的计划任务,则删除 Delete(lpszTaskName); // 创建任务定义对象来创建任务 ITaskDefinition *pTaskDefinition = NULL; HRESULT hr = m_lpITS->NewTask(0, &pTaskDefinition); if(FAILED(hr)) { ShowError("ITaskService::NewTask", hr); return FALSE; } /* 设置注册信息 */ IRegistrationInfo *pRegInfo = NULL; CComVariant variantAuthor(NULL); variantAuthor = lpszAuthor; hr = pTaskDefinition->get_RegistrationInfo(&pRegInfo); if(FAILED(hr)) { ShowError("pTaskDefinition::get_RegistrationInfo", hr); return FALSE; } // 设置作者信息 hr = pRegInfo->put_Author(variantAuthor.bstrVal); pRegInfo->Release(); /* 设置登录类型和运行权限 */ IPrincipal *pPrincipal = NULL; hr = pTaskDefinition->get_Principal(&pPrincipal); if(FAILED(hr)) { ShowError("pTaskDefinition::get_Principal", hr); return FALSE; } // 设置登录类型 hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN); // 设置运行权限 // 最高权限 hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST); pPrincipal->Release(); /* 设置其他信息 */ ITaskSettings *pSettting = NULL; hr = pTaskDefinition->get_Settings(&pSettting); if(FAILED(hr)) { ShowError("pTaskDefinition::get_Settings", hr); return FALSE; } // 设置其他信息 hr = pSettting->put_StopIfGoingOnBatteries(VARIANT_FALSE); hr = pSettting->put_DisallowStartIfOnBatteries(VARIANT_FALSE); hr = pSettting->put_AllowDemandStart(VARIANT_TRUE); hr = pSettting->put_StartWhenAvailable(VARIANT_FALSE); hr = pSettting->put_MultipleInstances(TASK_INSTANCES_PARALLEL); pSettting->Release(); /* 创建执行动作 */ IActionCollection *pActionCollect = NULL; hr = pTaskDefinition->get_Actions(&pActionCollect); if(FAILED(hr)) { ShowError("pTaskDefinition::get_Actions", hr); return FALSE; } IAction *pAction = NULL; // 创建执行操作 hr = pActionCollect->Create(TASK_ACTION_EXEC, &pAction); pActionCollect->Release(); /* 设置执行程序路径和参数 */ CComVariant variantProgramPath(NULL); CComVariant variantParameters(NULL); IExecAction *pExecAction = NULL; hr = pAction->QueryInterface(IID_IExecAction, (PVOID *)(&pExecAction)); if(FAILED(hr)) { pAction->Release(); ShowError("IAction::QueryInterface", hr); return FALSE; } pAction->Release(); // 设置程序路径和参数 variantProgramPath = lpszProgramPath; variantParameters = lpszParameters; pExecAction->put_Path(variantProgramPath.bstrVal); pExecAction->put_Arguments(variantParameters.bstrVal); pExecAction->Release(); /* 创建触发器,实现用户登陆自启动 */ ITriggerCollection *pTriggers = NULL; hr = pTaskDefinition->get_Triggers(&pTriggers); if (FAILED(hr)) { ShowError("pTaskDefinition::get_Triggers", hr); return FALSE; } // 创建触发器 ITrigger *pTrigger = NULL; hr = pTriggers->Create(TASK_TRIGGER_LOGON, &pTrigger); if (FAILED(hr)) { ShowError("ITriggerCollection::Create", hr); return FALSE; } /* 注册任务计划 */ IRegisteredTask *pRegisteredTask = NULL; CComVariant variantTaskName(NULL); variantTaskName = lpszTaskName; hr = m_lpRootFolder->RegisterTaskDefinition(variantTaskName.bstrVal, pTaskDefinition, TASK_CREATE_OR_UPDATE, _variant_t(), _variant_t(), TASK_LOGON_INTERACTIVE_TOKEN, _variant_t(""), &pRegisteredTask); if(FAILED(hr)) { pTaskDefinition->Release(); ShowError("ITaskFolder::RegisterTaskDefinition", hr); return FALSE; } pTaskDefinition->Release(); pRegisteredTask->Release(); return TRUE;}
    删除任务计划BOOL CMyTaskSchedule::Delete(char *lpszTaskName){ if(NULL == m_lpRootFolder) { return FALSE; } CComVariant variantTaskName(NULL); variantTaskName = lpszTaskName; HRESULT hr = m_lpRootFolder->DeleteTask(variantTaskName.bstrVal, 0); if(FAILED(hr)) { return FALSE; } return TRUE;}
    程序测试在 main 函数中调用上述封装好的函数,进行测试。main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ CMyTaskSchedule task; BOOL bRet = FALSE; // 创建 任务计划 bRet = task.NewTask("520", "C:\\Users\\DemonGan\\Desktop\\520.exe", "", ""); if (FALSE == bRet) { printf("Create Task Schedule Error!\n"); } // 暂停 printf("Create Task Schedule OK!\n"); system("pause"); // 卸载 任务计划 bRet = task.Delete("520"); if (FALSE == bRet) { printf("Delete Task Schedule Error!\n"); } printf("Delete Task Schedule OK!\n"); system("pause"); return 0;}
    测试结果:
    “以管理员身份运行程序”方式打开程序,提示任务计划创建成功。

    打开任务计划列表进行查看,发现“520”任务计划成功创建。



    然后,删除创建的任务计划,程序提示删除成功。

    查看任务计划列表,发现“520”任务计划已经成功删除。

    所以,测试成功。
    总结这个功能的实现涉及到COM组件的相关知识,所以初学者对此可能感到陌生,这是很正常的。其实,对于这方面的理解我也没有什么好的建议,只有多动手练几遍,加深程序逻辑和印象吧。
    注意的地方就是,创建任务计划,要求程序必须要有管机员权限才行。所以,测试的时候,不要忘记以管理员身份运行程序。
    参考参考自《Windows黑客编程技术详解》一书
    2 留言 2018-12-20 12:17:10 奖励15点积分
  • python实现对人脸的实时监测

    使用python实现对人脸的实时监测并将人脸保存到本地文件夹下1、先导入库【可能需要下载一下dlib包,注意一下版本是否对应】
    #导入库import cv2 import dlib import numpy as np2、开始编写#定义脸部特征检测器detector = dlib.getfrontal_face_detector() #得到人脸的正面cap = cv2.VideoCapture(0) #0代表打开摄像头frame_count = 0 #检测的帧数face_count = 0 #每一帧检测的人脸数margin = 0.2 # 边距比例while True:ret, frame = cap.read() #从摄像头获取的视频文件中读取第一帧if (ret != True): #判断摄像头是否正常打开并读取视频print(‘没有捕获到图像,请检查摄像头工作情况!’)breakinput_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) #BGR转为RGBframe_count +=1 #帧数加一img_h,img_w, = np.shape(input_img) #获取图像的尺寸img_h:高,img_w:宽,第三个代表通道detected = detector(frame,1)#对当前帧检测faces = [] #存放当前帧中的脸部数量1代表的是上采样if len(detected) > 0: #当前帧检测到脸部,至少检测到一个 for i, d in enumerate(detected): #enumerate枚举函数,一个个的进行检测 face_count += 1#人脸数增加 # 脸部图像坐标与尺寸,分别表示左上角坐标,右下角坐标,和长宽,加一代表的是: x1, y1, x2, y2, w, h = d.left(), d.top(), d.right() + 1, d.bottom() + 1, d.width(), d.height() # 用边距做调整 #限制最小是0 xw1 = max(int(x1 - margin * w), 0) yw1 = max(int(y1 - margin * h), 0) #img_w-1是:限制最大的就是img-1 xw2 = min(int(x2 + margin * w), img_w - 1) yw2 = min(int(y2 + margin * h), img_h - 1); # 脸部图像坐标 face = frame[yw1:yw2 + 1, xw1:xw2 + 1, :]#加一是为了保存边界 file_name = "./dataset/valid/one/" + str(frame_count) +'_one'+str(i) + '.jpg'#文件路径 cv2.imwrite(file_name,face) #绘制人脸边框 cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)#显示当前帧cv2.imshow("Face Detector", frame) # 按回车键终止视频检测if cv2.waitKey(1)&0xFF == 13: #1代表1ms,13代表回车键,27代表ESC键,ord('q') break3、打印运行结果print(‘已经完成了 {0} 帧检测,保存了 {1} 幅脸部图像’.format(frame_count, face_count))4、关闭摄像头和窗口cap.release()cv2.destroyAllWindows()
    3 留言 2020-10-14 14:36:47 奖励30点积分
  • 内核驱动模块及隐藏指定驱动-支持32位和64位Win7至Win10

    背景记得自己自学 Windows Rootkit 方面的技术编程的时候,基本上就是自己看书,然后跟着书上那过时的例子,一遍一遍地敲代码实现测试。之所以说是过时的,因为那只是在 XP 系统上有效,对于 XP 以上系统都不适用了,由于将这方面的书都比较老的缘故。不过大家放心,如果你看到我写的教程,那么大部分都是会支持 32 位和 64 位 Win7 至 Win10 全平台。
    本文主要讲解的是基于 LDR_DATA_TABLE_ENTRY 双链表实现遍历内核驱动模块,这个遍历支持 32 位和 64 位全平台。同时,讲解下 32 位系统下摘链隐藏驱动模块的实现方法,这种方式之所以不支持 64 位,是因为会修改到系统内核内存,会触发 Patch Guard,导致蓝屏。当然,我们也可以通过 ZwQuerySystemInformation 函数来实现遍历内核模块和 HOOK 它实现模块隐藏,这种方式,实际上也是基于 LDR_DATA_TABLE_ENTRY 结构实现的。所以,本文介绍的方法,是较为底层的。
    实现原理我们初学 Windows 内核编程的时候,都首先会接触到 DRIVER_OBJECT 这个驱动对象结构体:
    typedef struct _DRIVER_OBJECT { CSHORT Type; CSHORT Size; PDEVICE_OBJECT DeviceObject; ULONG Flags; PVOID DriverStart; ULONG DriverSize; PVOID DriverSection; PDRIVER_EXTENSION DriverExtension; UNICODE_STRING DriverName; PUNICODE_STRING HardwareDatabase; PFAST_IO_DISPATCH FastIoDispatch; PDRIVER_INITIALIZE DriverInit; PDRIVER_STARTIO DriverStartIo; PDRIVER_UNLOAD DriverUnload; PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION+ 1];} DRIVER_OBJECT;
    本文主要使用到 DriverSection 这个成员变量,这个成员变量指向的是结构体 LDR_DATA_TABLE_ENTRY。对于 LDR_DATA_TABLE_ENTRY 结构体的定义,网上有很多版本,但是,很多都是不能支持所有 32 位和 64 位全平台。下面我给出我自己总结的这个结构体的定义,这个定义是经过我多次编程测试,最终确定的,支持 32 和 64 位全平台系统:
    // 注意32位与64位的对齐大小#ifndef _WIN64 #pragma pack(1) #endiftypedef struct _LDR_DATA_TABLE_ENTRY{ LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; PVOID EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY ForwarderLinks; LIST_ENTRY ServiceTagLinks; LIST_ENTRY StaticLinks;} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;#ifndef _WIN64 #pragma pack()#endif
    可以从上面知道,从 LDR_DATA_TABLE_ENTRY 结构体中,我们可以获取驱动模块加载的基址、路径、名称等信息。
    其中,要着重理解 InLoadOrderLinks 这个成员变量。这个链表成员变量是一个双链表结构,指向上一个或者下一个内核驱动模块的 LDR_DATA_TABLE_ENTRY 结构。所以,我们只要对这个双链表遍历,就可以获得内核的所有模块信息。所谓隐藏模块的实现,也是通过摘除双链表中指定模块实现的。摘除双链表中的内核模块,可以使得依靠遍历双链表来实现的内核模块遍历的方法失效,同时也不影响内核模块的正常运行。
    要注意一点就是,上面的结构中,32 位和 64 位下的结构对齐问题,否则会出错。
    编码实现驱动模块遍历BOOLEAN EnumDriver(PDRIVER_OBJECT pDriverObject){ // 从PDRIVER_OBJECT获取DriverSection,便可获得驱动模块链表 PLDR_DATA_TABLE_ENTRY pDriverData = (PLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection; // 开始遍历双向链表 PLDR_DATA_TABLE_ENTRY pFirstDriverData = pDriverData; do { if ((0 < pDriverData->BaseDllName.Length) || (0 < pDriverData->FullDllName.Length)) { // 显示 DbgPrint("BaseDllName=%ws,\tDllBase=0x%p,\tSizeOfImage=0x%X,\tFullDllName=%ws\n", pDriverData->BaseDllName.Buffer, pDriverData->DllBase, pDriverData->SizeOfImage, pDriverData->FullDllName.Buffer); } // 下一个 pDriverData = (PLDR_DATA_TABLE_ENTRY)pDriverData->InLoadOrderLinks.Flink; } while (pFirstDriverData != pDriverData); return TRUE;}
    驱动模块摘链隐藏// 驱动模块隐藏BOOLEAN HideDriver(PDRIVER_OBJECT pDriverObject, UNICODE_STRING ustrHideDriverName){ // 从PDRIVER_OBJECT获取DriverSection,便可获得驱动模块链表 PLDR_DATA_TABLE_ENTRY pDriverData = (PLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection; // 开始遍历双向链表 PLDR_DATA_TABLE_ENTRY pFirstDriverData = pDriverData; do { if ((0 < pDriverData->BaseDllName.Length) || (0 < pDriverData->FullDllName.Length)) { // 判断是否为隐藏的驱动模块 if (RtlEqualUnicodeString(&pDriverData->BaseDllName, &ustrHideDriverName, TRUE)) { // 摘链隐藏 pDriverData->InLoadOrderLinks.Blink->Flink = pDriverData->InLoadOrderLinks.Flink; pDriverData->InLoadOrderLinks.Flink->Blink = pDriverData->InLoadOrderLinks.Blink; break; } } // 下一个 pDriverData = (PLDR_DATA_TABLE_ENTRY)pDriverData->InLoadOrderLinks.Flink; } while (pFirstDriverData != pDriverData); return TRUE;}
    程序测试在 Win7 32 位下,驱动成功执行:

    在 Win10 64 位下,驱动成功执行:

    总结这个内核模块遍历关键一点就是对 LDR_DATA_TABLE_ENTRY 结构体的定义声明,然后再对成员变量 InLoadOrderLinks 双向链表结构循环遍历以及摘链即可。
    大家要注意区分 LDR_DATA_TABLE_ENTRY 结构体在 32 位和 64 位系统下的结构体对齐,否则程序会出错。
    0 留言 2021-09-05 09:16:11 奖励38点积分
  • Flask多进程与多线程的区别

    Flask 默认是单进程,单线程阻塞的任务模式,在项目上线的时候可以通过 nginx+gunicorn 的方式部署 flask 任务。
    但是在开发的过程中如果想通过延迟的方式测试高并发怎么实现呢,其实非常简单。app.run() 中可以接受两个参数,分别是 threaded 和 processes,用于开启线程支持和进程支持。

    threaded:多线程支持,默认为 False,即不开启多线程
    processes:进程数量,默认为 1

    开启方式:
    if __name__ == '__main__': app.run(threaded=True) # app.run(processes=True)
    注意:多进程或多线程只能选择一个,不能同时开启。
    processes=num 使用报错了:ValueError: cannot have a multithreaded and multi process server,将 num 改为 True 就可以使用 Flask 框架的多进程了。
    多进程进程其实是资源的分配的单位,包括代码、内存、CPU 等等,多进程类似程序的多开,比如 qq 的多开。

    上图解释了多进程工作的原理:在主进程下,子进程 1 和子进程 2 分别复制了主进程的代码以及资源,而子进程 1 则只运行 test1 这个函数,子进程 2 则只运行 test2 这个函数,进程之间的全局变量互不影响,对资源的开销比较大。当主进程结束后,所有的子进程全部结束,而子进程之间互不影响。
    多线程在一个进程中我们也可以使用多任务,这就是线程,线程其实是操作系统资源调度的单位。多线程可以共享全局变量。
    多线程并不会复制主进程的代码和资源,而是共享全局变量,相比多进程来说,资源开销更加小。在同一时间,子线程 1 和子线程 2 同时运行,实现多任务,而他们会共享全局变量。
    例如设置一个全局变量,子线程让他 +1 后打印,另一个子线程打印也是 +1 后的数值。
    多线程和多进程的选择使用如果你对自己的硬件资源很自信,那么就选择多进程。因为多进程占用资源比较多,因为其全局变量不共享,内存中存储多个相同变量。
    如果你的代码逻辑很复杂,一些变量产生了问题,那么就不要用多线程了,宁愿使用多一些资源也不要产生故障。
    0 留言 2021-08-11 09:42:46 奖励29点积分
显示 0 到 15 ,共 15 条
eject