分类

课内:
不限
类型:
不限 毕业设计 课程设计 小学期 大作业
汇编语言 C语言 C++ JAVA C# JSP PYTHON PHP
数据结构与算法 操作系统 编译原理 数据库 计算机网络 软件工程 VC++程序设计
游戏 PC程序 APP 网站 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
年份:
不限 2018 2019 2020

资源列表

  • 基于JAVA的俄罗斯方块游戏

    一、功能需求俄罗斯方块应有如下功能:

    在二维的平面里用各种随即产生的方块堆积木,每填满一行消去一行,当到达顶部时,游戏结束玩家能通过方向键来控制方块的转动,左移,右移和直落每种类型的方块都有颜色游戏能够在玩的过程中,给出玩家的分数,分数是由随即方块的类型决定的,每堆一个方块,就将分数累加到总分中游戏有暂停、开始和结束、游戏规则、游戏说明等控制

    二、分析与设计
    开发环境:Eclipse
    软件功能架构图:

    界面设计过程:
    游戏主类,继承自JFrame类,负责游戏的全局控制。 内含:

    一个GameCanvas画布类的实例对象(用于存放小方格)一个保存当前活动块(RussiaBlock)实例的对象一个保存当前控制面板(ControlPanel)实例的对象添加菜单栏,并设置窗口的布局管理器为BorderLayout,左边加上画布,右边加上控制面板,设置整个程序打开时居中放置
    创意:玩家可以直接从控制面板控制游戏的开始和暂停以及增减难度,而且可以在菜单栏中更改游戏界面的背景色和方块的颜色,更加提高了用户舒适度。菜单中帮助栏目的关于本游戏的按钮可以显示出本人的信息。
    游戏界面图如下:




    程序实现的关键:

    该小程序总共由5个主要的类构成:

    菜单栏功能图

    各个类的关系如下图所示


    三、程序截图












    3 评论 294 下载 2018-10-21 20:40:18 下载需要14点积分
  • 基于C++的库存管理系统设计与实现

    一 需求分析1.1 总体要求运用面向对象程序设计知识,利用C++语言设计和实现一个“库存管理系统设计”,主要完成对商品的销售、统计和简单管理。在实现过程中,需利用面向对象程序设计理论的基础知识,充分体现出C++语言关于类、继承和封装等核心概念,每一个类应包含数据成员和成员函数。
    1.2 功能分析超市中商品分为四类,分别是食品、化妆品、日用品和饮料。每种商品都包含商品名称、价格、库存量和品牌等信息。
    本系统要求具备如下主要功能:
    1.2.1 商品简单管理功能
    添加功能:主要完成商品基本信息的添加
    查询功能:可按商品类别、商品名称、生产厂家、进货日期进行查询。若存在相应信息,输出所查询的信息,若不存在该记录,则提示“该记录不存在!”
    修改功能:可根据查询结果对相应的记录进行修改
    删除功能:主要完成商品信息的删除。先输入商品类别,再输入要删除的商品名称,根据查询结果删除该物品的记录,如果该商品不在物品库中,则提示“该商品不存在”

    1.2.2 进货功能按要求添加相应商品的信息到库存中。可按要求输入商品编号,商品名称,生产厂家,商品价格,商品数量,商品类别,入库时间等商品基本信息。
    1.2.3 出货功能出货时可按照商品名称查找相应商品,显示相关商品库存量等基本信息。若有库存量则输入出售数量,出售价格以及出库时间,计算销售额,利润。如果库存量不足则提示出货失败,结束出货。
    1.2.4 统计功能输出当前库存中所有商品的总数及详细信息;能统计每种商品一周时间内的销售额和利润;能统计每类商品的一周时间内的销售额和利润。输出统计信息时,要按从大到小进行排序。(根据个人能力,至少实现一种统计功能)
    二 程序设计与实现2.1 概要设计在定义商品的类别时采用了枚举类型,并定义Food=1,因此在程序中阿拉伯数字1,2,3,4即分别代表食品,化妆品,日用品,饮料这四种商品。
    系统类的关系图如下所示:

    2.1.1 系统的类层次在定义商品基本信息时采用了结构体类型,即定义了一个structGoods,其中包含类别,名称,品牌,价格,数量等信息,同时包含struct Date表示入库时间,包含enum GoodsType表示商品类别。
    定义class GoodsManage包含各成员函数进行对商品信息的管理,其中包含DisplayMainMenu(),AddGoodsInfo(),DisplayGoodsInfo(),SearchByCode()等成员函数。
    系统的类层次关系图如下图所示:


    2.1.2 主程序流程如下图所示:

    2.2 详细设计2.2.1 各类的描述商品类别
    //商品类别enum GoodsType{ Food=1, //食品 Cosmetic, //化妆品 Commodity, //日用品 Drink //饮料};
    入库时间
    //入库时间struct Date{ int year; int month; int day;};
    ((商品基本信息**
    //商品基本信息struct Goods{ string code; //商品编号 string name; //商品名称 string brand; //生产厂家 double price; //商品价格 int num; //商品数量 GoodsType type;//商品类别 Date date; //入库时间 Goods *next;};
    商品售出信息
    //商品售出信息struct SellRecord{ Goods sellGoods; //已出售的商品 int sellNum; //出售数量 double sellPrice;//出售价格 Date date; //出库日期 SellRecord *next;};
    商品管理
    //商品管理class GoodsManage{public: GoodsManage(); ~GoodsManage(){} void DisplayMainMenu(); //主菜单显示 void AddGoodsInfo(); //添加商品信息 void DisplayGoodsInfo();//浏览商品信息 void SearchByCode(); //按照商品编号搜索商品信息 void SearchByName(); //按照商品名称搜索商品信息 void SearchByType(); //按照商品类别搜索商品信息 void SearchByBrand(); //按照商品品牌搜索商品信息 void EditGoodsInfo(); //编辑商品信息 void DeleteGoodsInfo(); //删除商品信息 void SellGoodsInfo(); //出售商品信息 void SaveGoodsInfo(); //保存商品信息private: int amount; //商品量 int DeleteAmount; Goods *head; Goods *DeleteHead;};
    2.2.2 成员函数定义构造函数
    //定义构造函数GoodsManage::GoodsManage(){ amount=0; DeleteAmount=0; head=new Goods; head->next=NULL; DeleteHead=new Goods; DeleteHead->next=NULL;}
    2.主菜单显示函数
    //定义主菜单函数void GoodsManage::DisplayMainMenu(){ cout<<" ━═☆┈━═☆┈━═☆┈━═☆┈━═☆┈━═☆┈━═☆┈━═☆━═☆\n"; cout<<" ┋ ┋\n"; cout<<" ┋ 欢迎使用商品库存管理系统 ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ 【商品进货】…(a) ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ 【商品编辑】…(b) ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ 【商品删除】…(c) ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ ┋【按照编号查询】…(d) ┋\n"; cout<<" ┋ ┋ ┋\n"; cout<<" ┋ ┋【按照名称查询】…(e) ┋\n"; cout<<" ┋ 【商品查询】┋ ┋\n"; cout<<" ┋ ┋【按照类别查询】…(f) ┋\n"; cout<<" ┋ ┋ ┋\n"; cout<<" ┋ ┋【按照品牌查询】…(g) ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ 【商品出货】…(h) ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ 【商品统计】…(i) ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ 【信息保存】…(j) ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ ┋\n"; cout<<" ┋ 退出系统…(k) ┋\n"; cout<<" ┋ ┋\n"; cout<<" ━═☆┈━═☆┈━═☆┈━═☆┈━═☆┈━═☆┈━═☆┈━═☆━═☆\n"; cout<<"\n 请输入你要进行的操作编号:";}
    添加商品信息函数
    //定义添加商品信息函数voidGoodsManage::AddGoodsInfo(){ char c,c1; Goods tail=head,p; bool flag; cout<<" ☆☆☆☆☆☆现在进行商品信息的添加☆☆☆☆☆☆ "<<endl; while(tail->next!=NULL) tail=tail->next; do { flag=0; p=new Goods; cout<<"请选择商品类别:"<<endl; cout<<"1.食品 2.化妆品3.日用品 4.饮料"<<endl; cout<<"请输入相应编号:"; do { cin>>c1; if(c1>='1'&&c1<='4')//判断用户输入编号是否存在 flag=1; else { cout<<"您输入的编号不存在!"<<endl; cout<<"请选择正确的商品类别:"<<endl; } }while(flag==0);//输入编号存在时跳出循环 if(c1=='1') p->type=Food; if(c1=='2') p->type=Cosmetic; if(c1=='3') p->type=Commodity; if(c1=='4') p->type=Drink; cout<<"商品类别("<<p->type<<")"<<endl;//表示商品类别 cout<<"请输入商品编号: "; cin>>p->code; do { Goods *q=head->next; while(q!=NULL&&q->code!=p->code) q=q->next; if(q==NULL) flag=1; else { cout<<"存在该编号的货物!!!请重新输入编号:"; cin>>p->code; } }while(flag==0); cout<<"请输入商品名称:"; cin>>p->name; cout<<"请输入生产厂家:"; cin>>p->brand; cout<<"请输入商品价格:"; cin>>p->price; cout<<"请输入商品数量:"; cin>>p->num; cout<<"请输入入库时间(年/月/日):"; cin>>p->date.year>>p->date.month>>p->date.day; tail->next=p; p->next=NULL; tail=p; amount++;//商品量加一 cout<<"数据输入成功!!!想继续添加吗(y/n):"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!!!<请输入y/n>"<<endl; cout<<"数据添加成功!!!想继续输入吗(y/n):"; cin>>c; } }while(c=='y'); cout<<endl; cout<<"……信息处理完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar();}
    统计商品信息函数
    //定义商品信息浏览函数void GoodsManage::DisplayGoodsInfo(){ Goods *p=head; cout<<" ☆☆☆☆☆☆现在进行商品信息的浏览☆☆☆☆☆☆ "<<endl; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; while(p->next!=NULL) { p=p->next; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; } cout<<endl; cout<<"……信息统计完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar();}
    按照编号查找商品信息函数
    //按照商品编号查找商品信息void GoodsManage::SearchByCode(){ char c; Goods *p; bool flag; string FoundCode; cout<<" ☆☆☆☆☆☆现在进行商品信息的查找☆☆☆☆☆☆ "<<endl; do { p=head; flag=0; cout<<"请输入您要查找的商品编号:"; cin>>FoundCode; while(p->next!=NULL) { p=p->next; if(p->code==FoundCode) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; break; } if(flag==0) { cout<<"对不起,您查询的商品不存在!!!"<<endl; cout<<"您想要继续查找吗(y/n):"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!<请输入y/n>:"<<endl; cout<<"您想要继续查找吗(y/n):"; cin>>c; } } } }while(c=='y'); cout<<endl; cout<<"……信息查找完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar();}
    按照名称查找商品信息函数
    //按照商品名称查找商品信息void GoodsManage::SearchByName(){ char c; Goods *p; bool flag; string FoundName; cout<<" ☆☆☆☆☆☆现在进行商品信息的查找☆☆☆☆☆☆ "<<endl; do { p=head; flag=0; cout<<"请输入您要查找的商品名称:"; cin>>FoundName; while(p->next!=NULL) { p=p->next; if(p->name==FoundName) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; break; } if(flag==0) { cout<<"对不起,您查询的商品不存在!!!"<<endl; cout<<"您想要继续查找吗(y/n):"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!<请输入y/n>:"<<endl; cout<<"您想要继续查找吗(y/n):"; cin>>c; } } } }while(c=='y'); cout<<endl; cout<<"……信息查找完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar();}
    按照类别查找商品信息函数
    //按照商品类别查找商品信息void GoodsManage::SearchByType(){ char c; Goods *p; bool flag; int FoundType; cout<<" ☆☆☆☆☆☆现在进行商品信息的查找☆☆☆☆☆☆ "<<endl; do { p=head; flag=0; cout<<"请输入您要查找的商品类别:"; cin>>FoundType; while(p->next!=NULL) { p=p->next; if(FoundType==1&&p->type==Food) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; } else if(FoundType==2&&p->type==Cosmetic) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; } else if(FoundType==3&&p->type==Commodity) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; } else if(FoundType==4&&p->type==Drink) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; } if(flag==0) { cout<<"对不起,您查询的商品不存在!!!"<<endl; cout<<"您想要继续查找吗(y/n):"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!<请输入y/n>:"<<endl; cout<<"您想要继续查找吗(y/n):"; cin>>c; } } } }while(c=='y'); cout<<endl; cout<<"……信息查找完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); etchar();}
    按照品牌查找商品信息函数
    //按照品牌查找商品信息void GoodsManage::SearchByBrand(){ char c; Goods *p; bool flag; string FoundBrand; cout<<" ☆☆☆☆☆☆现在进行商品信息的查找☆☆☆☆☆☆ "<<endl; do { p=head; flag=0; cout<<"请输入您要查找的商品品牌:"; cin>>FoundBrand; while(p->next!=NULL) { p=p->next; if(p->brand==FoundBrand) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; break; } if(flag==0) { cout<<"对不起,您查询的商品不存在!!!"<<endl; cout<<"您想要继续查找吗(y/n):"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!<请输入y/n>:"<<endl; cout<<"您想要继续查找吗(y/n):"; cin>>c; } } } }while(c=='y'); cout<<endl; cout<<"……信息查找完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar();}
    编辑商品信息函数
    //定义编辑商品信息函数void GoodsManage::EditGoodsInfo(){ char c; Goods *p; bool flag=0; string EditCode; cout<<" ☆☆☆☆☆☆现在进行商品信息的编辑☆☆☆☆☆☆ "<<endl; do { p=head->next; flag=0; cout<<"请输入您要修改的货物编号:"; cin>>EditCode; while(p->next!=NULL&&p->code!=EditCode) p=p->next; if(p->code==EditCode) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; cout<<"确认修改吗?<y/n>"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!!<请输入y/n>:"; cin>>c; } if(c=='y') { cout<<"请输入商品名称:"; cin>>p->name; cout<<"请输入生产厂家:"; cin>>p->brand; cout<<"请输入商品价格:"; cin>>p->price; cout<<"请输入商品数量:"; cin>>p->num; cout<<"请输入入库时间(年/月/日):"; cin>>p->date.year>>p->date.month>>p->date.day; cout<<"修改成功!"<<endl; } else cout<<"取消成功!"<<endl; } if(flag==0) { cout<<"对不起,您修改的货物不存在!!"<<endl; } cout<<"您想要继续修改吗?(y/n):"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!<请输入y/n>:"<<endl; cout<<"您想要继续修改吗?(y/n):"; cin>>c; } }while(c=='y'); cout<<endl; cout<<"……信息编辑完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar();}
    删除商品信息函数
    //定义商品信息删除函数void GoodsManage::DeleteGoodsInfo(){ Goods q=head,p,*tail=DeleteHead; p=new Goods; char c; string Dename; bool flag=0; while(tail->next!=NULL) tail=tail->next; cout<<" ☆☆☆☆☆☆现在进行商品信息的删除☆☆☆☆☆☆ "<<endl; do { cout<<"请输入您要删除的商品名称:"; cin>>Dename; while(q->next!=NULL&&q->next->name!=Dename) q=q->next; if(q->next!=NULL) { flag=1; cout<<"确认删除吗?<y/n>"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!!<请输入y/n>:"; cin>>c; } if(c=='y') { p=q->next; q->next=q->next->next; tail->next=p; tail=p; tail->next=NULL; DeleteAmount++; amount--; cout<<"删除成功!!"<<endl; } else cout<<"取消成功!!!"<<endl; } if(flag==0) { cout<<"对不起,您删除的商品不存在!!!"<<endl; } cout<<"您想要继续删除吗?(y/n):"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!<请输入y/n>:"<<endl; cout<<"您想要继续删除吗?(y/n):"; cin>>c; } flag=0; q=head; }while(c=='y'); cout<<endl; cout<<"……信息删除完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar();}
    出售商品信息函数
    //定义商品出库函数void GoodsManage::SellGoodsInfo(){ int sellNum,year,month,day; doublesellPrice,sum=0.0,profit=0.0; char c; Goods *p; bool flag=0; string EditName; cout<<" ☆☆☆☆☆☆现在进行商品的出售☆☆☆☆☆☆ "<<endl; do { p=head->next; flag=0; cout<<"请输入您要出售的商品名称:"; cin>>EditName; while(p->next!=NULL&&p->name!=EditName) p=p->next; if(p->name==EditName) { flag=1; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; cout<<"确认出售吗?<y/n>"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!!<请输入y/n>:"; cin>>c; } if(c=='y') { cout<<"请输入出售的商品数量:"; cin>>sellNum; if(sellNum<=p->num) { p->num=p->num-sellNum; cout<<"请输入出售的商品价格:"; cin>>sellPrice; cout<<"请输入出货日期:"; cin>>year>>month>>day; sum=sellNum*sellPrice; profit=sellNum*(sellPrice-p->price); cout<<"此次销售额为: "<<sum<<endl; cout<<"此次利润为: "<<profit<<endl; cout<<"出货日期为:"<<year<<"/"<<month<<"/"<<day<<endl; } else { cout<<"库存不足!出库失败!"<<endl; } } else cout<<"取消成功!"<<endl; } if(flag==0) { cout<<"对不起,您出售的货物不存在!!"<<endl; } cout<<"您想要继续出售吗?(y/n):"; cin>>c; while(c!='y'&&c!='n') { cout<<"指令错误!!!<请输入y/n>:"<<endl; cout<<"您想要继续出售吗?(y/n):"; cin>>c; } }while(c=='y'); cout<<endl; cout<<"……出库完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar();}
    保存商品信息函数
    //定义商品信息保存函数void GoodsManage::SaveGoodsInfo(){ Goods *p=head; cout<<" ☆☆☆☆☆☆现在进行商品信息的保存☆☆☆☆☆☆ "<<endl; ofstream output("货物信息.txt",ios::out);//定义输出文件"货物信息.txt" if(!output) { cerr<<"打开文件<货物信息.txt>失败!!!"<<endl; } output<<amount<<"\n"; cout<<setiosflags(ios::left)<<setw(10)<<"编号"<<setw(16)<<"名称"<<setw(10)<<"生产厂家"<<setw(10)<<"价格"<<setw(10)<<"商品类别"<<setw(10)<<"数量"<<setw(10)<<"入库时间"<<endl; while(p->next!=NULL) { p=p->next; output<<p->code<<"\t"<<p->name<<"\t"<<p->brand<<"\t"<<p->price<<"\t"<<p->num<<"\t"<<p->type<<"\t"<<p->date.year<<"\t"<<p->date.month<<"\t"<<p->date.day<<"\n"; cout<setiosflags(ios::left)<<setw(10)<<p-code<<setw(16)<<p->name; cout<<setw(10)<<p->brand<<setw(10)<<p->price<<setw(10)<<p->type; cout<<setw(10)<<p->num<<p->date.year<<"/"<<p->date.month<<"/"<<p->date.day<<endl; } cout<<endl; cout<<"成功将货物信息保存到<货物信息.txt>"<<endl; cout<<"……信息保存完毕……"<<endl; cout<<"……按任意键返回主菜单……"<<endl; getchar(); getchar(); output.close();//关闭输出文件}
    2.2.3 算法流程图添加商品信息函数流程如下所示:

    浏览商品信息函数流程如下所示:

    按照商品编号搜索商品信息函数流程如下所示:

    按照商品名称搜索商品信息函数流程如下所示:

    按照商品类别搜索商品信息函数流程如下所示:

    按照商品品牌搜索商品信息函数流程如下所示:

    编辑商品信息函数流程如下所示:

    删除商品信息函数流程如下所示:

    出售商品信息函数流程如下所示:

    保存商品信息函数流程如下所示:

    三 运行测试程序主菜单

    添加商品信息

    编辑商品信息

    删除商品信息

    按照编号搜索商品信息

    按照类别搜索商品信息

    按照名称搜索商品信息

    按照品牌搜索商品信息

    出售商品信息

    保存商品信息

    统计商品信息
    6 评论 179 下载 2018-10-18 22:21:00 下载需要14点积分
  • 基于VC++的MFC类库实现的简单FTP客户端

    1 FTP客户端设计思想在WINDOWS环境下,使用VC++开发工具实现一个FTP客户端软件。在本次FTP的设计中主要使用WinInet API编程,无需考虑基本的通信协议和底层的数据传输工作,MFC提供的WinInet类是对WinInet API函数封装而来的,它为用户提供了更加方便的编程接口。而在该设计中,使用的类包括 CInternetSession类、CFtpConnection类和CFtpFileFind类,其中,CInternetSession用于创建一个Internet会话; CftpConnection完成文件操作; CftpFileFind负责检索某一个目录下的所有文件和子目录。
    程序的功能:

    登陆到FTP服务器
    检索FTP服务器上的目录和文件
    根据FTP服务器给的权限,会相应地提供:文件的上传、下载、重命名、删除等功能

    2 Ftp客户端的各个子模块主要函数功能分析及流程图2.1 对程序的外观进行修改2.1.1 添加背景图为CMyFtpView类窗口,添加一个位图背景显示,首先为CmyFtpView添加WM_ERASEBKGND消息响应函数OnEraseBkgnd。该函数的前提是所需的位图已经导入资源中。
    2.1.2 添加时钟显示功能首先为CMainFrame类,设置一个定时器,然后为该类响应WM_TIMER消息,在OnTimer函数中实现功能。
    2.1.3 修改菜单栏、状态栏在资料对话框或源程序代码中采取针对性的操作,得以实现。
    2.2 连接到FTP服务器功能的实现2.2.1 生成连接对话框新建一个对话框(CConnectDlg)用来输入服务器的站点,用户名和密码信息,然后通过该对话框连接到服务器.主要的函数void CConnectDlg::OnConnect() 实现的功能就是更新当前的输入。
    2.2.2 连接CMyFtpView::OnConnect()该函数是菜单项“连接”的响应函数,主要生成“连接”CConnectDlg对话框,从而建立FTP连接。
    2.2.3 连接时间void CMyFtpView::OnTimer对CMyFtpView定时器,所发送的WM_TIMER消息进行响应,主要用于监视FTP连接是否成功连接。
    2.3 FTP客户端文件的显示查询实现2.3.1 查询函数OnQuary该函数是“查询”按钮BN_CLICKED的响应函数,主要调用ListContent函数为实现查询的功能
    2.3.2 显示当前目录下所有的子目录与文件ListContent参数所代表的是要查询的目录名,实现的方法主要是通过CftpFileFind类对象得以实现,返回给用户的信息有:目录或文件名、文件最后修改的时间以及文件的大小和类型。
    流程图如下所示:

    2.3.3 下一级目录函数OnNextdirectory该函数是“下一级目录”BN_CLICKED的响应函数,当用户选中一个子目录时,点击按纽就会进入该目录,调用了主要函数:GetCurrentDirectory 、SetCurrentDirectory、ListContent。
    流程图如下所示:

    2.3.4 上一级目录函数OnLasttdirectory该函数是“上一级目录”BN_CLICKED的响应函数,点击按纽就会返回到该目录的上一级目录,调用了主要函数:GetCurrentDirectory 、SetCurrentDirectory、ListContent。
    流程图如下所示:

    2.4 FTP客户端部分功能实现2.4.1 下载函数OnDownLoad为了下载列表中的某一个文件,首先判断是否选中了项目,否则提示没有选择文件,然后得到选择的项目的类型是否是文件,,如果是文件,则得到下载的文件名,下载文件(调用CFtpConnect类中的GetFile函数下载文件)如果选中的是目录,则弹出对话框,不能下载目录。
    流程图如下所示:

    2.4.2 上传函数OnUpLoad上传函数首先得获得想要上传的本地文件的路径名和文件名,弹出打开对话框,找到所要上传的文件后使用PutFile函数上传文件(调用CFtpConnect类中的PutFile函数),等函数上传完后提示是否上传成功,最后调用查询函数,显示新的文件列表。
    流程图如下所示:

    2.4.3 删除函数OnDelete删除函数首先判断是否选择项目,如果没有,则弹出对话框没有选择文件。选择了项目后,得到选择项的类型,如果是目录,则提示不能删除目录,然后用m_pConnection->Remove删除文件并提示是否删除成功,最后调用查询函数,更新文件列表。
    2.4.4 重命名函数OnRename:重命名文件需要新建立一个对话框(CNewNameDlg),后选择要重命名的文件,没有选择项目,提示没有选择文件,激活控件后,弹出新建的对话框,输入新的文件名后用m_pConnection->Rename重新命名文件,最后调用查询函数,更新文件列表。
    3 主要的数据结构分析CMyFtpView类
    CConnectDlg m_ConDlg; //用于连接CFtpDlg m_FtpDlg; //用于创建一个CFtpDlg对话框,进行操作控制CString m_FtpWebSite; //服务器站点,用于输入服务器的站点名称或IP地址CString m_UserName; //登陆服务器的用户名称CString m_UserPwd; //登陆服务器的密码CInternetSession * m_pSession; //通过CConnectDlg 的设置,得到一个Internet会话CFtpConnection* m_pConnection; //通过m_pSession来实现一个FTP连接
    CFtpDlg类
    CButton m_BtnQuery; //查询按扭CButton m_BtnUpLoad; //上传按扭CButton m_BtnDownLoad; //下载按扭CString m_NewFileName; //保存文件的新名称CListCtrl m_FtpFile; //用于显示目录和文件信息CFtpConnection* m_pConnection; //代表所建立的FTP连接CFtpFileFind* m_pFileFind; // CFtpFileFind指针,用于查找文件
    4 Ftp客户端的各个子模块设计过程及代码分析4.1 对程序的外观进行修改4.1.1 删除工具栏在CmainFrame的OnCreate函数中,删除与工具栏m_wndToolBar相关的代码,因为在本程序中没有提供相应的工具栏。
    4.1.2 修改菜单将原有的菜单项删除,增加“连接”和“退出客户端”子菜单项,(去掉Pop-up前的勾,同时赋以ID值),同时,为“连接”和“退出客户端”子菜单项添加COMMAND消息响应,响应的类分别为CMyFtpView类和CMainFrame类.响应函数为OnConnect和OnExit,其中:
    OnExit函数代码如下:
    void CMainFrame::OnExit() { //退出程序的响应函数 if(IDYES==MessageBox("确定要退出客户端吗?","警告",MB_YESNO|MB_ICONWARNING)) CFrameWnd::OnClose();}
    4.1.3 添加位图背景为CMyFtpView类窗口,添加一个位图背景显示,首先为CmyFtpView添加WM_ERASEBKGND消息响应函数,代码如下:
    //用于添加背景图BOOL CMyFtpView::OnEraseBkgnd(CDC* pDC) { CBitmap bitmap; //前提IDB_BITMAP2代表的位图已经导入资源中 bitmap.LoadBitmap(IDB_BITMAP2); CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); //创建与当前DC(pDC)兼容的DC,先用dcCompatible准备图像,再将数据复制到实际DC中 dcCompatible.SelectObject(&bitmap); CRect rect; GetClientRect(&rect); //得到目的DC客户区大小 BITMAP bmp; // BITMAP结构体,用于保存位图的信息 bitmap.GetBitmap(&bmp); //将兼容DC中的位图Copy到目标DC中 pDC->StretchBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,0,0, bmp.bmWidth, bmp.bmHeight,SRCCOPY); return true;}
    修改主窗口的大小,在PreCreateWindow函数中添加:cs.cx=450; cs.cy=550;
    4.1.4 修改状态栏使之只具有时间显示窗格,首先MainFrame.cpp中修改indicators,如下:
    static UINT indicators[] ={ ID_SEPARATOR, // status line indicator IDS_TIMER,};
    同时,在资源视图的String Table中添加字符串资源:IDS_TIMER,Caption为:时间。
    然后,添加时钟显示功能:

    在CMainFrame的OnCreate中添加代码:SetTimer(1,1000,NULL)来设置一个定时器
    为CMainFrame添加WM_TIMER的响应函数,代码如下:

    void CMainFrame::OnTimer(UINT nIDEvent) { //用于在状态栏显示当前时间 CTime t=CTime::GetCurrentTime(); CString str=t.Format("%H:%M:%S"); CClientDC dc(this); CSize sz=dc.GetTextExtent(str);//得到Str的长度,用于控制时间窗格 m_wndStatusBar.SetPaneInfo(1,IDS_TIMER,SBPS_NORMAL,sz.cx); m_wndStatusBar.SetPaneText(1,str);//将当前时间显示在时间窗格中 CFrameWnd::OnTimer(nIDEvent);}
    4.1.5 改变应用程序窗口标题标题改为“FTP客户端”在CmyFtpApp的InitInstance函数中添加代码:m_pMainWnd->SetWindowText(“FTP客户端”);
    4.2 连接到FTP服务器功能的实现4.2.1 插入一个对话框
    对各控件进行变量设置(注:在IDC_EDIT3编辑框的Styles选项中,选中PassWord),如下图所示:

    4.2.2 新建一个类管理对话框在打开ClassWizard时,系统提示是否新建一个类管理对话框,选择“是”,类名为:CconnectDlg,基类为:CDialog。
    4.2.3 CConnectDlg函数对按钮“连接”的鼠标的点击,进行响应,函数如下:
    void CConnectDlg::OnConnect() { UpdateData(); CDialog::OnOK();}
    4.2.4 添加public成员变量首先在CMyFtpView.h中添加public成员变量:
    CConnectDlg m_ConDlg; //管理连接对话框CFtpDlg m_FtpDlg; //管理CFtpDlgCString m_FtpWebSite; //保存Ftp服务器站点Cstring m_UserName; //保存用户名CString m_UserPwd; //保存用户密码CInternetSession* m_pSession; //用于Internet连接CFtpConnection* m_pConnection; //用于建立Ftp连接// 注: CFtpDlg为后建立的新类
    并在构造函数中初如化:
    CMyFtpView::CMyFtpView(){ m_FtpWebSite = _T(""); m_UserName = _T(""); m_UserPwd = _T(""); m_pSession = NULL; m_pConnection = NULL;}
    4.2.5 修改CMyFtpView类的OnConnect函数:代码如下:
    void CMyFtpView::OnConnect() { //生成一个模态对话框 if (IDOK==m_ConDlg.DoModal()) { m_pConnection = NULL; m_pSession = NULL; m_FtpWebSite = m_ConDlg.m_FtpWebSite; m_UserName = m_ConDlg.m_UserName; m_UserPwd = m_ConDlg.m_UserPwd; m_pSession=new CInternetSession(AfxGetAppName(), 1, PRE_CONFIG_INTERNET_ACCESS); try { //试图建立FTP连接 SetTimer(1,1000,NULL); //设置定时器,一秒发一次WM_TIMER CString str="正在连接中...."; ((CMainFrame*)GetParent())->SetMessageText(str); m_pConnection=m_pSession->GetFtpConnection(m_FtpWebSite, m_UserName,m_UserPwd); } catch (CInternetException* e) { //错误处理 e->Delete(); m_pConnection=NULL; } }}
    4.2.6 添加响应函数:为CMyFtpView类添加WM_TIMER消息的响应函数,代码如下:
    void CMyFtpView::OnTimer(UINT nIDEvent) { static int time_out=1; //用于判断是否超时 time_out++; //每秒增加一次 if (m_pConnection == NULL) { CString str="正在连接中...."; ((CMainFrame*)GetParent())->SetMessageText(str); //在状态栏中显示,连接状态 if (time_out>=60) //设置超时的时间为1分钟 { ((CMainFrame*)GetParent())->SetMessageText("连接超时!"); KillTimer(1); //关闭定时器 MessageBox("连接超时!","超时",MB_OK); //提醒用户 } } else //如果连接成功,执行如下 { CString str="连接成功!"; ((CMainFrame*)GetParent())->SetMessageText(str); KillTimer(1); //连接成功之后,不用定时器来监视连接情况 //同时跳出操作对话框 //将FTP连接交给CFtpDlg m_FtpDlg.m_pConnection = m_pConnection; //创建非模态对话框CFtpDlg m_FtpDlg.Create(IDD_DIALOG2,this); m_FtpDlg.ShowWindow(SW_SHOW); } CView::OnTimer(nIDEvent);}
    4.3 FTP客户端主要功能的实现4.3.1新插入一个对话框资源:新插入一个对话框资源,界面如下:

    各控件变量信息如下图所示:

    4.3.2 建立新类CFtpDlg管理该对话框建立新类CFtpDlg管理该对话框,基类:CDialog。
    构造函数代码:
    CFtpDlg::CFtpDlg(CWnd* pParent /*=NULL*/): CDialog(CFtpDlg::IDD, pParent){ m_pConnection = NULL; m_pFileFind = NULL;}
    4.3.3 初始化对话框为CFtpDlg添加WM_INITDIALOG响应函数OnInitDialog,代码如下:
    BOOL CFtpDlg::OnInitDialog() { CDialog::OnInitDialog(); //设置CListCtrl对象的属性 m_FtpFile.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); //设置列 m_FtpFile.InsertColumn(0,"文件名",LVCFMT_CENTER,200); m_FtpFile.InsertColumn(1,"日期",LVCFMT_CENTER,100); m_FtpFile.InsertColumn(2,"字节数",LVCFMT_CENTER,100); //初如化CftpFileFind类对象m_pFileFind m_pFileFind = new CFtpFileFind(m_pConnection); //调用OnQuary函数查询FTP服务器当前目录下的目录和文件信息 OnQuary(); return TRUE; }
    4.3.4 查询函数OnQuary为查询按纽添加BN_CLICKED响应函数OnQuary,代码如下:
    void CFtpDlg::OnQuary() { ListContent("*"); }
    手动为CFtpDlg添加函数ListContent:
    void CFtpDlg::ListContent(LPCTSTR DirName){ m_FtpFile.DeleteAllItems(); BOOL bContinue; bContinue=m_pFileFind->FindFile(DirName); if (!bContinue) { //查找完毕,失败 m_pFileFind->Close(); m_pFileFind=NULL; } CString strFileName; CString strFileTime; CString strFileLength; while (bContinue) { bContinue = m_pFileFind->FindNextFile(); strFileName = m_pFileFind->GetFileName(); //得到文件名 //得到文件最后一次修改的时间 FILETIME ft; m_pFileFind->GetLastWriteTime(&ft); CTime FileTime(ft); strFileTime = FileTime.Format("%y/%m/%d"); if (m_pFileFind->IsDirectory()) { //如果是目录不求大小,用<DIR>代替 strFileLength = "<DIR>"; } else { //得到文件大小 if (m_pFileFind->GetLength64() <1024) { strFileLength.Format("%d B",m_pFileFind->GetLength64()); } else { if (m_pFileFind->GetLength64() < (1024*1024)) strFileLength.Format("%3.3f KB", (LONGLONG)m_pFileFind->GetLength64()/1024.0); else { if (m_pFileFind->GetLength64()<(1024*1024*1024)) strFileLength.Format("%3.3f MB", (LONGLONG)m_pFileFind->GetLength64()/(1024*1024.0)); else strFileLength.Format("%1.3f GB", (LONGLONG)m_pFileFind->GetLength64()/(1024.0*1024*1024)); } } } int i=0; m_FtpFile.InsertItem(i,strFileName,0); m_FtpFile.SetItemText(i,1,strFileTime); m_FtpFile.SetItemText(i,2,strFileLength); i++; } }
    4.3.5 下一级目录函数OnNextdirectory为”下一级目录”按纽添加BN_CLICKED响应函数OnNextdirectory,代码如下:
    void CFtpDlg::OnNextdirectory() { static CString strCurrentDirectory, strSub; //声明2个静态变量,// strCurrentDirectory表示当前目录名,strSub表示选定的文件名 //首先得到当前目录,保存在strCurrentDirectory m_pConnection->GetCurrentDirectory(strCurrentDirectory); strCurrentDirectory+="/"; //在当前取得的目录名后添加“/” //得到所选择的文本,即所要进入的下一级目录名 int i=m_FtpFile.GetNextItem(-1,LVNI_SELECTED); //判断文件是否选中 strSub = m_FtpFile.GetItemText(i,0);//将选中的文件名保存至strSub if (i==-1) { AfxMessageBox("没有选择目录!",MB_OK | MB_ICONQUESTION); } else { //判断是不是目录 if ("<DIR>"!=m_FtpFile.GetItemText(i,2))//显示文件的第三列非<DIR> { AfxMessageBox("不是子目录!",MB_OK | MB_ICONSTOP); } else { //设置当前目录 m_pConnection->SetCurrentDirectory(strCurrentDirectory+strSub); //对当前目录进行查询,更新ClistCtrl控件的内容 ListContent("*"); } }}
    4.3.6 上一级目录函数OnLasttdirectory为”上一级目录”按纽添加BN_CLICKED响应函数OnLastdirectory,代码如下:
    //返回上一级目录void CFtpDlg::OnLastdirectory() { static CString strCurrentDirectory; //声明为静态变量 m_pConnection->GetCurrentDirectory(strCurrentDirectory); if (strCurrentDirectory == "/") { AfxMessageBox("已经是根目录了!",MB_OK | MB_ICONSTOP); } else { //调用函数GetLastDiretory,得到上一级目录名 GetLastDiretory(strCurrentDirectory); //设置当前目录为上一级目录 m_pConnection->SetCurrentDirectory(strCurrentDirectory); //对当前目录进行查询,更新ClistCtrl控件的内容 ListContent("*"); }}
    为CFtpDlg类添加一工具函数GetLastDiretory用于得到上一级目录的名称。
    //用于得到上一级目录的字符串表示void CFtpDlg::GetLastDiretory(CString &str){ int LastIndex=0;//定义一个初始整形变量 for (int i=0; i<str.GetLength(); i++)//定义i取得当前地址字符串长度 { if (str.GetAt(i)=='/')//追溯至数组的最后一个“/” LastIndex = i;//得到当前的数值“i” } //删除最后一个’/’之后的字符串 str = str.Left(LastIndex);//自带的left()函数:返回参数前的所有字符串 if (LastIndex == 0)//当追溯的本身即为根目录 str="/";}
    4.4 FTP客户端部分功能的实现4.4.1 下载函数OnDownLoad为”下载”按纽添加BN_CLICKED响应函数OnDownLoad,代码如下:
    void CFtpDlg::OnDownload() { int i=m_FtpFile.GetNextItem(-1,LVNI_SELECTED); //先判断是否已经选中文件 if (i==-1) //如果没有被选中 { //弹出对话框提示没有选择文件 AfxMessageBox("没有选择文件!",MB_OK | MB_ICONQUESTION); } else { //如果选中了文件,则得到选择项的类型,判断是不是文件 CString strType=m_FtpFile.GetItemText(i,2); if (strType!="<DIR>") //选择的是文件 { CString strDestName; //下载后的文件名 CString strSourceName; //原文件名 //得到所要下载的文件名 strSourceName = m_FtpFile.GetItemText(i,0); CFileDialog dlg(FALSE,"",strSourceName); //弹出SAVE AS对话框 if (dlg.DoModal()==IDOK) { //获得下载文件在本地机上存储的路径和名称 strDestName=dlg.GetPathName(); //调用CFtpConnect类中的GetFile函数下载文件 if (m_pConnection->GetFile(strSourceName,strDestName)) AfxMessageBox("下载成功!",MB_OK|MB_ICONINFORMATION); else AfxMessageBox("下载失败!",MB_OK|MB_ICONSTOP); } } else { //选择的是目录 AfxMessageBox("不能下载目录!\n请重选!",MB_OK|MB_ICONSTOP); } }}
    4.4.2 上传函数OnUpLoad为”上传”按纽添加BN_CLICKED响应函数OnUpload,代码如下:
    void CFtpDlg::OnUpload() { //获得当前输入 CString strSourceName; //原文件名 CString strDestName; CFileDialog dlg(TRUE,"","*.*"); if (dlg.DoModal()==IDOK) { //获得待上传的本地机文件路径和文件名 strSourceName = dlg.GetPathName(); strDestName = dlg.GetFileName(); //调用CFtpConnect类中的PutFile函数上传文件 if (m_pConnection->PutFile(strSourceName,strDestName)) AfxMessageBox("上传成功!",MB_OK|MB_ICONINFORMATION); else AfxMessageBox("上传失败!",MB_OK|MB_ICONSTOP); } //更新ClistCtrl的内容 OnQuary();}
    4.4.3 删除函数OnDelete为”删除”按纽添加BN_CLICKED响应函数OnDelete,代码如下:
    //删除选择的文件void CFtpDlg::OnDelete() { int i=m_FtpFile.GetNextItem(-1,LVNI_SELECTED); if (i==-1) { AfxMessageBox("没有选择文件!",MB_OK | MB_ICONQUESTION); } else { CString strFileName; strFileName = m_FtpFile.GetItemText(i,0); if ("<DIR>"==m_FtpFile.GetItemText(i,2)) { AfxMessageBox("不能删除目录!",MB_OK | MB_ICONSTOP); } else { if (m_pConnection->Remove(strFileName)) AfxMessageBox("删除成功!",MB_OK|MB_ICONINFORMATION); else AfxMessageBox("无法删除!",MB_OK|MB_ICONSTOP); } } OnQuary();}
    4.4.4 重命名函数OnRename新插入一个对话框资源,界面如下:

    控件变量信息如下图所示:

    改写对话框的OnOK函数,代码如下:
    void CNewNameDlg::OnOK() { UpdateData(); CDialog::OnOK();}
    为”重命名”按纽添加BN_CLICKED响应函数OnRename,代码如下:
    void CFtpDlg::OnRename() { CString strNewName; CString strOldName; //得到CListCtrl被选中的项 int i=m_FtpFile.GetNextItem(-1,LVNI_SELECTED); if (i==-1) { AfxMessageBox("没有选择文件!",MB_OK | MB_ICONQUESTION); } else { strOldName = m_FtpFile.GetItemText(i,0);//得到所选择的文件名 CNewNameDlg dlg; if (dlg.DoModal()==IDOK) { strNewName=dlg.m_NewFileName; if (m_pConnection->Rename(strOldName,strNewName)) AfxMessageBox("重命名成功!",MB_OK|MB_ICONINFORMATION); else AfxMessageBox("无法重命名!",MB_OK|MB_ICONSTOP); } } OnQuary();}
    4.4.5 退出函数OnExit为”退出”按纽添加BN_CLICKED响应函数OnExit,代码如下:
    void CFtpDlg::OnExit() { m_pConnection = NULL; m_pFileFind = NULL; DestroyWindow(); //销毁对话框资源}
    5 测试程序5.1 连接操作
    5.2 连接成功后
    此后可根据FTP服务器所提供的权限进行相应的操作。
    6 整个设计过程中遇到的主要问题6.1 如果已经连上了某服务器,而在没有退出程序之前,再点连接,就会有问题了
    原因
    CFtpDlg在调用OnExit函数的时候仅仅是隐藏了对话框,并没有销毁它,导致了m_FtpDlg的重复创建m_FtpDlg.Create(IDD_DIALOG2,this);此行代码是问题关键,点退出时,并没有销费
    解决之道
    OnExit函数中调用DestryWindow,而不是CDialg::OnCancel;

    6.2 在进入下一级目录的函数编写过程中,只能进入第二层目录
    原因
    假设当前目录为 “/”,进入第一层子目录”FTP”之后,当前目录变为“/FTP”,再想进入FTP目录下的”MyFtp”子目录的时候,之前的代码,仅仅是实现了“/FTP”+”MyFtp”简单加法,即当前目录为”/FTPMyFtp”,虽然是不对的。
    解决之道
    在获取子目录名称之前,使当前目录所代表的字符串加上符号‘/’ ,代码表示为:

    strCurrentDirectory+="/";
    6.3 在返回上一级目录的函数编写过程中,无法返回到根目录
    原因
    比如:在”/FTP”之下,返回到上一级目录,原代码将导致当前目录strCurrentDirectory为空,如果运行m_pConnection->SetCurrentDirectory(strCurrentDirectory),显然也是不对的
    解决之道
    在GetLastDiretory中添加判断:当前目录是否为空,如果是,则设strCurrentDirectory=”/”;代码实现为:

    if (LastIndex == 0) str="/";
    7 总结在这次课程设计中,我们小组通过从各个方面查找资料,了解了WinInet API编程的基本知识,熟悉了MFC编程中的一些控件知识,通过这次设计锻炼了我们组员的团队协作能力。
    通过对FTP这种大型的设计,我们组成员深感我们平时的编程习惯与良好的编程习惯相差甚远,小组成员决定在以后的编程过程中养成良好的编程习惯,这样有助于自己所编的程序清晰明了便于该错还有助于别人来立解你的程序。同时通过这次课程设计我们形成了通过从各方面查找资料来丰富自己的知识的能力。
    1 评论 143 下载 2018-10-31 22:08:14 下载需要13点积分
  • 基于JAVA实现的超级马里奥(Super Mario)游戏

    一、项目简介刚进入的时候会有一个界面,为地图编辑器。可以使用此编辑器进行地图编辑,地图编辑器的内容包括:关卡、向左箭头、带有金币的砖块、带有花朵的砖块带有蘑菇的砖块、带有星星的砖块、普通砖块、向左运动的板栗仔、向右运动的板栗仔、向左运动的乌龟、向右运动的乌龟、金币、带有食人花的管道、普通管道、洞、向右的箭头、橡皮擦、可以使用鼠标点击图标然后拖动到面板上点击面板进行地图编辑,橡皮擦可以擦除已经建立好的模型。
    部署完地图之后可以选择下一关进行下一个关卡的编辑,也可以点击开始游戏开始游戏。游戏开始后从编辑的第一关卡开始进行闯关,人物可以移动通过ad键进行控制,可以跳跃,通过k控制,跳的时候可以跳到管子和砖块上面。
    人物有两种状态:大马里奥和小马里奥。小玛丽奥可以撞普通的砖块或者带有包含物的砖块使得砖块可以向上稍微移动,砖块上的一些包含物也会随着砖块移动。大马里奥可以顶破普通砖块。
    怪物分为三种,分别为:板栗仔、乌龟和食人花。马里奥可以通过跳跃的方式踩死怪物,板栗仔在被踩的时候会变扁,乌龟被踩的时候。走动状态会变成龟壳状态,龟壳状态被碰到可以变成跑动的龟壳状态,跑动的龟壳可以杀死马里奥。板栗仔和其他的乌龟。运动的龟壳在运动的时候被马里奥踩到会变成静止的龟壳,食人花长在管道中,会定时出现对管道上方的物体进行攻击,当马里奥踩在管道上的时候不会出现。
    还有三种物体是包含在砖块中的,分别是星星、蘑菇、花朵。马里奥自下向上顶砖块之后砖块上方会生长出相应的植物星星和蘑菇会向右方向行走,花朵会在原地。马里奥可以通过触碰的方式吃掉植物,不同植物有不同的加成效果。其中,吃掉蘑菇之后会变成大马里奥。吃掉星星之后会变成无敌状态。吃掉花朵之后会有发射子弹的技能。
    任何物品,尤其是可移动物,包括子弹,在碰到洞之后会掉落到洞中人物掉落之后会损失一命,人物一共有五条生命。每次正面碰到乌龟或者板栗仔,或者掉落到洞中之后便会损失一条生命,每次损失生命则该关卡从头开始当五条生命全部损失之后便会到game over状态。
    当马里奥走到地图的最后一个模型之后的位置的时候说明本关通过,本关通过时会有马里奥跳下拉动旗帜旗帜拉倒底端的时候会向右跑到城堡位置。跑到城堡位置即属于本关卡已经通过,则消除所有的加成状态转到下一关卡。
    最后通关所有的关卡即为game ends,跳跃的时候有重力效应,降落的会越来越快游戏界面上方会有剩余生命,当前时间为0的时候会损失一命,还有计分系统。当玩家杀死怪物,或者吃掉某种可生长物,或者过关的时候都会获取相应的分数加成。分数显示在面板上方。吃掉金币会有金币数量统计,统计结果在生命右边。本项目的亮点在于应用ioc技术的地图编辑器和精美的人物模型。
    二、需求分析人物跳跃的重力,条约落下的时候碰到其他的硬物可以停止下落。踩死怪物。怪物死亡方式不同展现的画面不同。吃东西,有属性加成。大马里奥顶破砖块。小玛丽奥顶砖块砖块可跟随移动。砖块上方的东西可以跟随移动。地图编辑功能。声音功能。人物胜利拉旗进城堡。
    三、系统设计
    本项目共有20个公共类,5个接口。分成三种类,分别是:控制类、模型类、工具类
    功能方面有两大块,分别是地图编辑器和正式游戏
    地图编辑器部分使用了spring框架的ioc技术

    程序功能流程图如下图所示:

    控制类中有两个类分别为Main和Control作用分别为控制地图编辑,地图编辑的思路如下:玩家点击图标之后鼠标的状态变成点击的图标的状态值,本类中有一个map键值分别为Integer和model鼠标移动到某位置点击之后会使integer加一。构造出相应的model然后put到map中如果用户点击的是橡皮擦。那么会计算哪一个类在橡皮擦点击的位置,并且把相应的位置的model设为忽略。最后解析这个map构造xml文件和保存着各个类的数量的一个properties文件Control类通过解析xml和properties文件解析关卡信息还原用户编辑的地图。
    还有一个全局的properties为game.properties保存着关卡数目。在点击开始游戏之后开始运行Control类中的work方法。Work方法的作用是初始化整个游戏的完整页面读入xml中的内容实例化对象存到容器中。然后启动paintThread线程画出面板,启动其他的必要线程进行工作,根据用户的操作对容器中的对象的一些参数进行改变呈现不同的视觉效果。
    四、系统实现本项目由于需要实现用户自由设计地图,所以应该尽量降低耦合度,从全局的角度出发,对类的设计应该分为实物模型,统筹控制,工具,抽象出来的接口。接下来一一介绍。类结构图如下图所示:

    统筹控制包含Main和Control在上一部分已经介绍。接口被抽象出来以下几种:

    Dangerous类所有的可以杀死主角的实物模型类应该去实现该接口
    Flint类,砖块和管子应该去实现该接口。Growable接口,所有的可以被马里奥从砖块中顶出的实物模型应该实现该接口。Kill接口,所有的可以伤害到别的实物模型类的类都应该实现该接口。Moveable接口,所有的可以移动的物体都应该实现该接口

    实物模型类一共有12个:

    Badflower类是食人花类,实现Dangerous接口
    Bullet类是子弹类,实现Moveable和Kill接口
    Flower类是吃了以后可以发射子弹的类,实现了Growable接口
    Hole类是地面上存在的洞类。没有去实现任何接口
    Mario类,是主角类,实现了Moveable,Kill接口
    Money类,因为可以直接被主角吃掉并且在砖块上被顶出之后不需要生长移动过程所以不实现任何接口
    Monster类是板栗仔,实现了Dangerous,Moveable接口
    Mushroom类是吃了以后变大的蘑菇类实现了Growable和Moveable接口
    Pipe是管道类实现了Flint接口
    Star是吃了以后变成无敌状态的星星,实现了Growable和Moveable接口
    Turtle是乌龟类,实现了Dangerous,Kill,Moveable接口
    Wall是砖块类,实现了Flint方法

    还有6个工具类,其中的方法和字段大部分是静态的和final的:

    ApplicationUtil类可以通过传入的关卡值去加载spring上下文,为程序提供对象实例
    CrashType定义了一些物体之间的碰撞类型的常量
    ImageTool类中包含了程序用到的所有的图片以及为了克服延迟加载而写的事先加载所有图片的方法
    Null类是一个Growable类的空实现,因为在构造砖块的时候定义构造方法里面应当传入所包含的可生长物,而在使用spring框架进行实例化得时候不允许出现null.本人又极不愿意在写另外一种构造方法,所以索性构造一个Growable接口的空实现类,通过传出特殊的Type值进行识别
    Property类,用于解析配置文件,获取数据
    SoundTool类包含所有的使用到的音乐,以及静态的播放音乐的方法

    五、调试改错在实现的过程中出现了很多错误。比如声音播放问题和人物碰撞检测的问题等。不过最后解决的还算满意。
    六、美工素材本项目是一个人写的,代码和图片美工都是自己实现的。由于互联网上找不到相关素材,所以本人现学的ps,通过录制游戏中的人物动作分帧截图,使用抠图等技术自己做的图。
    七、总结要有统筹整个项目的意识。对整个项目有总体的把握。类的编写应当事先分好类,分清楚每种类的任务。最大化解耦,以免改动的时候涉及到过多的地方。分清楚每个类的任务。合理设计避免冗余。
    八、关键代码碰撞检测部分代码:
    public int getCrashType(int down,int direction,Rectangle rec1,Rectangle rec2)//rec1为wall,rec2为Mario 获取撞击类型 { //rec1是硬物。rec2是移动物 if(die)return CrashType.NO_CRASH; int rec1X=(rec1.x+rec1.x+rec1.width)>>1; int rec1Y=(rec1.y+rec1.y+rec1.height)>>1; int rec2X=(rec2.x+rec2.x+rec2.width)>>1; int rec2Y=(rec2.y+rec2.y+rec2.height)>>1; int width=rec1.width+rec2.width; int hight=rec1.height+rec2.height; if(rec2Y>=rec1Y) { if(rec1X>=rec2X) { if(down!=1||(rec1X-rec2X)/((double)width)>((rec2Y-rec1Y)/(double)hight)+CrashType.POINT) return CrashType.WALL_L;else { if(control.getMario().isCanWork())work(); control.getMario().down(); return CrashType.WALL_D; } }else { if(down!=1||(rec2X-rec1X)/((double)width)>((rec2Y-rec1Y)/(double)hight)+CrashType.POINT) return CrashType.WALL_R;else { if(control.getMario().isCanWork())work(); control.getMario().down(); return CrashType.WALL_D; } } }else { if(rec1X>=rec2X) { if((rec1X-rec2X)/((double)width)>=((rec1Y-rec2Y)/(double)hight)+CrashType.POINT) return CrashType.WALL_L;else { return CrashType.WALL_U; } }else { if((rec2X-rec1X)/((double)width)>=((rec1Y-rec2Y)/(double)hight)+CrashType.POINT)return CrashType.WALL_R;else return CrashType.WALL_U; } } }
    马里奥跳跃代码:
    private class JumpThread extends Thread//跳的线程 { private int n=jumpHight; public void run() { if(down==-1)return; if(downThread!=null) { downThread.stop(); downThread=null; } if(jumpThread!=null) { jumpThread.stop(); jumpThread=null; } jumpThread=this; //while(!Control.isALL_START()){try{sleep(Control.TIME);} catch (InterruptedException e){}} SoundTool.play(SoundTool.jumpSound); try { int site=locaY; double count=Math.sqrt((2*0.085*n)); for(int i=site;count>0;i-=(count-=0.1))//向上跳的时候改变状态 { if(getCrashType()&&(crashType==CrashType.WALL_D||crashType==CrashType.WUWL||crashType==CrashType.WUWR))//如果发现从下撞击了硬物则跳出向上的过程改为向下 { down=-1; break; } locaY=i; sleep(10); down=1; } new Down().start(); }catch(Exception e) { e.printStackTrace(); } down=0; } }
    马里奥降落代码:
    private class Down extends Thread//二类下落线程 //控制最终下落的线程如果在正常下落时由于碰撞被打断则启动该线程监视是否需要再次落下 { public void run() { if(down==1)return; //while(!Control.isALL_START()){try{sleep(Control.TIME);} catch (InterruptedException e){}} if(downThread!=null) { downThread.stop(); } downThread=this; // System.out.println("enter down!"); // while(down!=-1&&locaY!=control.getCutLine())//没有落地面上一直在循环 while(locaY<control.getCutLine()) { boolean flag=false;// int site=locaY; // System.out.println(crashType+" "+down+" "+canWork); if((getCrashType()&&(crashType==CrashType.NO_CRASH)&&(down!=1))||canWork==false)//多线程重要判断应该靠紧 { flag=true;//已经下落 double count=1; for(int i=site;i<=control.getCutLine();i+=(count+=0.1)) { down=-1; locaY=i; downSpeed=count; // System.out.println(i+" "+control.getCutLine()); if(getCrashType()&&(crashType==CrashType.WALL_U||crashType==CrashType.PIPE_U||crashType==CrashType.WUWL||crashType==CrashType.WUWR))//如果在某个硬物的上方、启动二类下落线程且退出本线程 { down=0;//落道硬物上面则运动状态为0 downSpeed=0; downThread=null; new Down().start(); setCanWork(true); return; } try { sleep(10); } catch (InterruptedException e) { down=0; downThread=null; downSpeed=1; setCanWork(true); new Down().start(); } } locaY=control.getCutLine(); } if(flag)//在该线程中如果已经下落则跳出 { if(!control.getMario().isDownDie()) for(int j=0;j<control.getHoles().size();j++) { if(control.getHoles().get(j).canPaint()) control.getHoles().get(j).DownDie(control.getMario()); } if(!control.getMario().isDownDie())downSpeed=1; down=0; downThread=null; setCanWork(true); return; } } }
    4 评论 282 下载 2018-10-31 12:18:12 下载需要11点积分
  • 带语音提示的图书管理系统

    一、需求分析
    完成简单的图书管理业务:

    新书入库:登记新书的编号、书名、作者和数量书目信息维护:删除、更新读者信息维护:新增、删除读者查询 借阅、归还
    语音提示功能:

    用户进行操作时语音提示,提高交互性

    二、功能实现
    数据库(c++实现) 关系类数据库

    索引——B树查询方式——sql存储方式—-顺序+索引
    语音功能

    c# mstts
    界面

    c#

    三、概要设计3.1 B树即二叉搜索树:

    所有非叶子结点至多拥有两个儿子(Left和Right)
    所有结点存储一个关键字
    非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树

    在项目中用做数据库的索引。
    3.2 SqlSQL是高级的非过程化编程语言,允许用户在高层数据结构上工作。他不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全 不同底层结构的不同数据库系统可以使用相同的SQL语言作为数据输入与管理的接口。它以记录集合作为操纵对象,所有SQL语句接受集合作为输入,返回集合 作为输出,这种集合特性允许一条SQL语句的输出作为另一条SQL语句的输入,所以SQL语言可以嵌套,这使他具有极大的灵活性和强大的功能,在多数情况 下,在其他语言中需要一大段程序实现的一个单独事件只需要一个SQL语句就可以达到目的,这也意味着用SQL语言可以写出非常复杂的语句
    在项目中为用户提供最简单的sql语句。
    3.3 TTSTTS是语音合成应用的一种,它将储存于电脑中的文件,如帮助文件或者网页,转换成自然语音输出。TTS可以帮助有视觉障碍的人阅读计算机上的信息,或者 只是简单的用来增加文本文档的可读性。现在的TTL应用包括语音驱动的邮件以及声音敏感系统。TTS经常与声音识别程序一起使用。现在有很多TTS的产 品,包括Read Please 2000, Proverbe Speech Unit,以及Next Up Technology的TextAloud。朗讯、 Elan、以及 AT&T都有自己的语音合成产品。
    本项目中作为发声引擎。
    四、详细设计4.1 程序流图
    4.2 图形界面4.2.1 登陆需提供用户名(默认为admin)和密码(默认为admin)。密码用md5+乱序的方式存放于DB/PWD.mdb(文本文件) :
    //混合密码public static string GetMd6Str(string ConvertString){ MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); string t2 = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(ConvertString)), 4, 8).Replace("-", "B"); t2 += BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(ConvertString)), 4, 8).Replace("-","Z"); t2 += BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(ConvertString)), 4, 8).Replace("-", "W"); return t2;}
    4.2.2 语音提示
    使用微软的TTS引擎
    语言程序安装包带有TTS5.1..
    优先使用windows7 vista 自带的TTS5.3(XP不支持LILI语音库..故会使用SAM)
    封装成DLL了 namespace TTS; 静态类speaker.speak(string thins);

    4.3 核心—数据库系统的类图
    索引采用B树..动态阶数..除根节点外每层的最大key为2/m+1。
    数据库系统有个public method query(std::string query)用于执行简单的sql语句。



    语句
    功能
    例子




    Create




    —database
    创建数据库
    create database std


    —table
    创建表 和字段数据
    create table `book` (`id` key,`booktitle` text,`publisher` text,`type` text,`writer` text,`maxnums` int,`lendnums` int)


    Use
    选择数据库
    use sda


    Insert
    插入数据
    insert into `book` values (`3`,`电子`,`电子工业`,`null`,`null`,`5`,`3`)


    Delete
    删除数据
    delete from `book` where `booktitle`=’汇编’


    Select
    查找数据
    select from `book` where `id`<=’7’


    Update
    更新数据



    数据库系统代码行约3000,update功能暂未实现..可以先delete然后再insert..
    查询时给入相应的sql语句,查询到的结果会存入result这个迭代器。
    外部封装成DLL,可调用参数:
    fetchline() 格式化结果,取一行query(string line) 执行sqlString^ get(String^ tablename) 取当前行的字段名下的字段值注:命名空间 DBcore。
    4.4 数据库文件物理系统DB│├─book│ data.dbs│ id.idx│├─lendinfo│ data.dbs│ id.idx│└─reader data.dbs sid.idxidx文件就是存放的B树索引文件,dbs是数据库主文件。当通过索引操作时读取索引文件进B树..其他情况下均顺序操作。
    五、调试与运行语音提示输入

    登陆界面

    登陆成功

    主界面可进行图书查询,读者查询等功能的应用

    新书入库


    图书查询..可按任意一项查询..书号唯一

    新读者登记

    借阅3本


    结果..库存减少..拥有数增加

    归还图书
    0 评论 2 下载 2020-07-01 13:45:27 下载需要15点积分
  • 基于ThinkPhp框架的高校图书馆藏书借阅系统

    一、概述使用了ThinkPHP,虽然本人觉得该框架实在有点反人类,但是也算是第一次使用PHP的框架。可以对于MVC有更深的理解。
    前端界面

    后台界面

    登录后台的默认管理员是:2333333333,密码:admin。
    二、功能设计2.1 数据库设计2.1.1 需求分析(1)系统任务我们所要做的是一个高校图书馆藏书借阅系统,用于读者对图书的检索、浏览、借阅。因此,需要的功能包括对图书的检索查询、借阅、预约、续借、安全性保障。
    而在拓展功能板块,我们的考虑为以图书为中心建立起图书与图书之间的关系,图书与人之间的关系,以图书为中心的推荐系统强调的是如何建立起有效的链接关系。
    (2)有需求的用户组成对此系统有需求的用户是有浏览、借阅图书需求的读者——由教师、学生、普通游客组成。其中,教师、学生是有借阅/预约图书资格的用户,而且借阅权限有所不同;普通游客是那些非本校的、可以浏览、检索此系统所藏图书而没有借阅和预约资格的用户。
    (3)用户的信息要求教师与学生使用此系统主要用于检索、浏览、借阅图书,包括查询书目信息,借书、还书,预约与续借。教师与学生为获得借阅资格,需要在此系统上进行用户注册并登陆,然后通过检索,并根据每本书的书目信息——其ISBN、标题、摘要、封面图、出版者、出版年份、中图法的图书分类号、价格来确定所借阅的图书。
    教师与学生还有对自身用户状态的信息需求,包括借阅记录、预约记录、信用值、是否被罚款等。同时,还有对所需图书的借阅状态的信息需求,比如只有知道此书是否已被他人预约而确定能否续借等。额外的需求还有图书推荐等。
    普通游客不需要进行用户注册和登陆,只是浏览,其需求包括了解此系统内所含图书的大体信息,比如标题、封面图、摘要等从而了解馆藏的大致情况。
    (4)系统边界对此系统的人工操作包括供图书馆管理员操作的图书的录入,新用户的等级,图书的修改,用户的修改等,而数据库管理员拥有上述的所有权限,并且能够定义用户的权限等。总体而言,DBA拥有对于系统的全部掌握。
    2.1.2 概念结构设计首先分析数据库描述的主要对象中有哪些实体,最主要的便是书和用户,此外还有拥有最高操作权限的数据库管理员。

    书和用户存在着:借阅、(反复)预约关系
    借阅会有如下情形:按时归还与逾期还书、丢失、续借

    预约则有如下要求:

    仅能在到期时间前5天进行续借
    如果此书已被人预约,则不可续借
    多人续借同一本书则按照预约时间进行排队

    特别注意的地方:

    作者和译者本是书的两个属性,但是现实中存在一本书存在多个作者和译者的情形,所以需要单独为作者和译者建立一个表(由于作者和译者没有属性上的差别,且同一个人既可能是作者有可能是另外书籍的译者),另外建立书-作者表和书-译者表
    图书有ISBN号作为同一版本同一书籍的唯一标识码,但是图书管中存在同一本书的多个复本,由此建立书目表和复本表。书目表记录同一书籍的书目信息,复本表记录各个复本的流通情况
    用户分为三类:学生读者、教师读者、图书馆管理员。其中学生读者和教师读者的借阅权限有所不同;图书馆管理员可以修改学生和教师的权限,修改书目信息,但不能修改自己的权限;数据库管理员可以修改三类用户的权限和数据库的其它信息

    E-R图如下:

    2.1.3 逻辑结构设计共有10张表:

    图书书目表
    图书复本表
    著者(即作者与译者)表
    书-作者表
    书-译者表
    用户表
    借阅记录表
    预约记录表
    数据库管理员表
    评论表

    各表的具体属性如下:
    图书书目表:yzz_book



    id
    ISBN
    abstract
    title
    cover
    publisher
    publish-year
    type
    price




    主码,图书的流水编号,用于唯一标记书。相对于ISBN更加易于修改和查询。
    国际标准书号,可以唯一标识书
    文摘
    标题
    图书封面
    出版者
    出版年份
    中图法的图书分类号
    书的价格



    图书复本表:yzz_copy



    id
    book_id
    is_borrowed
    is_booked
    location




    主码,复本的流水编号
    相应的图书的书目编号
    此复本是否被借出
    此复本是否被预约
    复本的馆藏位置



    著者(即作者与译者)表:yzz_creator



    id
    Name
    country
    birthday




    著者的流水编号
    姓名
    国家
    出生年份



    书-作者表:yzz_author



    author_id
    book_id




    作者的流水编号
    书目的流水编号



    书-译者表:yzz_translator



    translatorr_id
    book_id




    译者的流水编号
    书目的流水编号



    用户表



    id
    password
    name
    role
    gender
    depart
    birthday
    email
    credit
    book_num




    用户的密码
    密码
    姓名
    用户类别:教师/学生/图书馆管理员
    性别
    院系
    生日
    电子邮件
    信用值(如果读者逾期还书,则信用值下降,低于10则不可再借书,需要充值恢复信用)
    可借阅图书上限



    借阅记录表:yzz_book_borrow



    id
    userid
    bookid
    borrowtime
    returntime
    status
    xjcs




    借阅记录流水编号
    用户的账号
    复本的编号
    借出时间
    归还时间(未还则为NULL)
    还书:0未归还,1已归还
    续借次数



    预约记录表:yzz_book_booked



    id
    userid
    bookid
    booktime
    status




    预约记录流水编号
    用户的号
    复本的编号
    预约时间
    预约是否有效:仍在预约1,借书成功0



    数据库管理员表:DBA



    id
    password




    DBA账号
    密码



    评论表:remark



    id
    userid
    content
    bookid
    replyid




    评论的流水编号
    用户账号
    评论内容
    评论的书目的编号
    被复的评论的编号,如果为原创话题则为NULL



    2.2 RBAC角色结构2.2.1 RBAC:基于角色的访问控制(Role-Based Access Control)共有4个角色:普通游客,教师/学生,图书馆管理员,DBA。

    普通游客:可以进行检索、浏览,不能借阅或者预约
    教师/学生:除了检索或者浏览之外,登陆之后可以借阅/预约图书
    图书馆管理员:除了拥有“教师/学生”的角色的权限外,可以对“教师/学生”的操作记录、借阅和预约、信用值、借书上限进行修改

    DBA:数据库管理员对数据库拥有最高权限,可以修改教师/学生和图书馆管理员这两类角色的相关记录
    2.2.2 页面模块
    Admin:供图书馆管理员操作
    DBA:供数据库管理员操作
    User:供登录的在校用户操作
    Home:检索,在前端使用
    PublicUse:函数、功能部分,在登录时使用

    2.3 MVC我们采用了MVC软件构建模式, MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。其中:

    Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
    View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的
    Controller(控制器)是应用程序中处理用户交互的部分。可以接受访问并对访问做出应答。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据

    MVC的主要优点有:

    MVC 分层有助于管理复杂的应用程序。这是因为可以在一个时间内专门关注一个方面。例如,可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易
    MVC 分层同时也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑

    2.4 系统安全2.4.1 密码安全:MD5使用了MD5(Message Digest Algorithm 5),是目前应用最广泛的Hash算法,具有单向性、抗冲突性、映射分布均匀性和差分分布均匀性等关键特性。
    它的算法的特征是不可逆性,并且才计算的时候所有的数据都参与了运算,其中任何一个数据变化了都会导致计算出来的Hash值完全不同,所以通常用来校验数据是否正确或用作身份验证。
    我们数据库中的密码是经过MD5等Hash算法算出来的Hash值进行保存的。用户登录时将根据实时输入得到一个Hash值,与数据库中密文储存的密码相匹配。
    2.4.2 防止重复登录:Session记录Session相当于服务器端的缓存,通过Session的记录判断用户是否已经登录,如果不存在相应的记录则跳转到登陆界面。
    2.5 查找功能
    系统支持多字段进行检索,检索字段包括标题、文摘、作者等等
    图书检索的显示结果会分页显示,便于用户浏览

    2.6 图书的相关操作不同用户可以进行图书相关的操作如下:



    普通访客
    教师/学生
    图书馆管理员
    DBA




    查询书目信息





    借书、还书





    预约与续借





    书目信息修改






    注:DBA负责整个数据库的宏观维护,不参与具体的书籍流通过程。
    2.7 用户相关操作对用户的相关操作,仅对图书馆管理员和DBA两类角色开放:

    两类角色都可以进行新用户的注册与用户的删除
    修改数据库记录:图书馆管理员可以修改“教师/学生”的相关记录,但不能修改自己或者其他管理员的记录;DBA可以对用户授权,并修改其它所有用户的记录
    用户的借书上限和信用值和角色类型有关。比如教师、学生、图书馆管理员的借书上限分别为40、30、50。之后可由图书馆管理员和DBA进行修改。信用值默认为均为100

    2.8 记录的保存小组的数据库会存储借书、预约和续借的记录,这些日志记录可以用于之后的数据分析,进一步应用于文献的推荐,分析书籍与读者的内在联系,使得图书的流通过程成为可以强化图书馆服务职能的工具。
    2.9 还书提醒、罚款与缴费如果读者距离应还书时间仅有5天时,系统会提醒读者应该及时还书,并提供网上续借的跳转网页链接。当此复本没有被预约时,读者可以进行续借。
    如果读者逾期还书,那么相应地会产生罚款与催款两种体系。我们使用的是信用额,在其归还书的时候根据其超时进行超时的罚款。

    罚款:如果逾期还书,那么超时1天信用值下降1。信用值默认为100,当信用值下降到10以下时读者不可再借阅其他书籍
    缴费:当信用值下降到10以下时,读者需要向图书馆管理员缴纳欠款,由图书馆管理员手动修改回信用值

    三、数控设计3.1 参照完整性为了保证数据库系统正常运行,我们在系统内设置了外键,提供参照完整性方案。
    如在book_borrow、book_booked和book三个表中,book_borrow表中的bookid不是该表的主键,但和book表中的id对应,是外键。这种设定能保证在book_borrow中出现的书籍条目在数据库中都能找到,即被借出的书都是图书馆中存在的书。
    同样的,book_booked表中的bookid也是外键,这保证了在图书馆系统中被预定的书都是图书馆中存在的书。
    另外,在translator表和author表中的bookid也都是外键,这两个外键确保了在译者、著者表中的作者都能和系统中的书籍相对应。
    其中,translator是允许为空的,因为中文的书籍不需要翻译,而author是不允许为空的,因为各个图书都有对应的作者(编纂集体)。
    3.2 Ajax保证完整性在设计注册、借还等页面时,我们使用了异步javascript和XML技术,保证局部刷新时不影响整体页面,也不需要用户来回登录注册页面。
    Ajax是用于创建快速动态网页的技术,通过在后台和服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着不需要重新载入整个网页来显示新内容,而只需要进行局部的刷新来获得变更内容。
    在我们的系统中,用户注册时会被要求输入用户名,因为实际情况中可能使用的用户是非常多的,难免造成用户名一致的问题。当用户名与之前注册的用户一致时,会直接显示错误提示,要求用户更换用户名,而不需要在注册提交时再告知重复,让用户重新填写所有内容。
    另外,在借书时系统会要求用户登录自己的帐号,此时如果用户未注册,却输入了已经存在的用户名,也会提示用户名被注册。
    这种设计主要目的在于为用户节省时间,一方面保证了系统中的注册用户是唯一的,实现了系统完整性的要求;另一方面方便用户注册,不需要一次次输入全部注册信息。
    3.3 时间戳时间戳是一个字符序列,唯一地标识某一刻的时间。数字时间戳技术是数字签名技术的一种变种。在电子商务交易中,时间戳是重要信息,它和签名一样能防止文件或合同被篡改。
    在数据库中,在表上加时间戳来制作索引,能方便我们在后台查找某一时间内被借走的图书,方便管理员的工作。由于时间戳是按格林威治时间至今换算为秒来计算,基本上能够保证唯一性,在检查图书流通记录时是很好的依据。
    四、关键功能的算法设计4.1 ThinkPHP框架ThinkPHP是快速兼容简单的php开发框架,使用面向对象的开发结构和MVC模式,融合了Struts的思想和标签库、ROR的ORM映射和ActiveRecord模式。ThinkPHP能解决应用开发中的大多数需要,包含了底层架构、兼容处理、基类库、数据库访问层、模版引擎、缓存机制、插件机制、角色认证、表单处理等常用组件。
    在使用ThinkPHP框架时,我们能将更多的精力放在图书馆系统的逻辑设计、功能实现上,因为繁琐的基本功能或需要多次使用的程序段都能直接从组件中调用。在图书管理系统中,图书管理员的身份相对于用户和图书记录来说是长期身份,在保存到数据库后一段时间内不会变动。
    ThinkPHP中的静态模型功能能在访问数据库后生成缓存,之后再次查看管理员记录表时就不需要再次链接数据库,而直接从缓存文件中查看,能节省时间提高效率。
    另外,它还提供了灵活和方便的数据操作方法,实现了对数据库的创建、读取、更新和删除,方便在数据库构建过程中使用。
    4.2 ORM对象关系映射(ORM)是用于实现面向对象编程语言里不同类型系统的数据转换时采用的技术。面向对象是从计算机编程技术的角度衍生的,而关系数据库是从严格的数学模型推导衍生的,ORM为这两个不同领域工具的衔接提供了帮助。
    这使得我们在开发图书馆管理系统时,减少数据访问层的代码编写数量,不用频繁地涉及数据库的保存、删除、更新和插入操作。使用ORM保存、删除和更新对象,我们能更集中于功能的实现和解决逻辑问题,而SQL语句的输入和调用都能交给ORM来实现。
    4.3 CURDCURD表示创建、更新、读取和删除四个基本操作。它们在数据库中处于基础位置,主要通过关系型数据库系统中的结构化查询语言(SQL)实现的。
    我们的图书馆管理系统能为读者提供基本的读取查询操作,方便其浏览图书。图书管理员和系统管理员则可以参与到图书表内容的创建、更新和删除中。
    4.4 RBACRBAC是基于角色的访问控制。在RBAC中,权限与角色相关联,用户通过成为适当角色成员来获得这些角色的权限,能极大地简化权限的管理。在一个组织中,角色为了完成某种工作而创造,用户则依据他的责任和资格被委派相应的角色,并可以很容易的在角色之间转换。同时,角色可以因实际需要被赋予新的权限,也能随时收回被赋予的权限。
    在我们的图书馆系统中,使用者被分为了四个主要权限:系统管理员、图书管理员、学生和老师。他们分别使用不同的权限。如教师和学生都是直接用户,他们的权限包括浏览、借阅、预定和续借等;图书管理员还需要对图书进行登记,在归还时改变图书的状态,因而有操作图书表的权限;系统管理员对整个数据库进行维护,其权限范围更广。
    通过对不同的角色赋权,再将角色赋予用户,能极大地减少管理员工作,提高效率。
    4.5 文件上传在图书管理员后台,可以进行增加图书的操作。我们的系统不仅支持文字的录入,即书目相关信息、作者相关信息等内容的记录;还支持图片上传功能,当管理员登记一本图书时,他还可以选择上传图书封面图片,这样在读者借阅时就能直接浏览图书封面,来确定是否是自己需要的图书。
    文件上传功能还为图书馆功能拓展提供了帮助。如可以增加儿童图书馆,儿童在选择书籍时可能更关注书的颜色,对他们而言,鲜亮卡通的颜色搭配更能提高他们的注意力。这时因为我们在图书登记时就加入了封面,能很方便的进行图书颜色分类,实现功能拓展,而不需要重新对每本书的外观进行描述。
    4.6 其他4.6.1 分页在本次课程作业中,我们的初步设想是向图书馆系统中增加100本书,跟实际情况相比,100本书是很少的量,完全可以通过平铺浏览的方式直接显示出来。
    但考虑到实际工作中,图书馆系统面对的常常是十万册、百万册的图书,即便是精确检索的记录也会提供诸多近似结果。这时分页就显得尤为重要了。
    分页可以选择相关性最高的图书优先显示在第一页,如果这一页中有读者需要的书本,就能大量减少读者的使用时间,提高借阅效率。同时,如果读者采用的查询词不够精确,返回了大量结果,则可以分页显示来减少视觉压力,也能提高系统的反应速度,每一次响应时间变短,但切换页面时的响应次数变多,变相地减少读者的等待时间,提高用户体验。
    4.6.2 验证码验证码可以自动区分使用方是人还是计算机,可以防止恶意破解密码、刷票等恶意行为。对图书馆管理系统来说,在注册、借阅环节采用验证码,可以防止某些用户使用机器高频率、重复注册来破坏系统,或者对一个用户的密码进行多次尝试暴力破解,也能在借书和预定环节的逻辑判断上更加公平。
    在我们的系统中,注册时,用户需要参照给出的图片输入其中的数字和字母,如验证正确则判断是人工注册,可以进行下一步环节;如因机器注册而不能识别,则不允许进入注册环节。另外,用户还可以手动刷新验证码来选择有较高清晰度的图片进行验证。
    4.6.3 日志记录我们的图书馆系统在图书的每次变动后都会增加一条新记录。如读者A借阅了图书1,日志文件中就会生成一条新纪录。当图书1的借阅时间到期,而读者A还没有看完时,他可以选择续借,这时会生成新记录。而读者B需要图书1,则可以在我们的前端预定这本书,此时新增booked记录。当一本图书被预定又被借阅时,系统会判断先后,被预定的图书不允许再次续借,而预定会排在之前的续借完成后才会提醒。通过日志文件,我们就能清楚的分析图书流通过程。
    另一方面,当图书馆系统出现问题,如数据丢失、数据库损坏、病毒或人为破坏时,我们可以读取日志记录来恢复之前的借阅,并在新的记录中延续旧日志,实现图书记录工作长期化和有效化。
    五、进一步扩展完善的设想5.1 图书的近似匹配5.1.1 摘要的切词处理由于时间有限,无法收集图书全文信息,而只是录入了相应的摘要信息。因此本图书馆系统的全文检索实质上是根据用户输入的检索词与摘要的匹配来实现的。单纯的全文匹配显然无法满足用户需求,很有可能会遗漏许多和用户需要有高度相关性却和检索词稍有区别的图书。于是我们便不可避免的遇到了摘要的切词处理问题,只有通过合适的中文切分词算法,将摘要自动切分,便可以进行精准的词匹配。
    而进行切词之前,还需要使用停用词表,去掉在每篇文摘中都会出现的高频词,如“的”,“本书”之类的词语。当去掉这些词语之后,倒排档的规模会得到极大的缩减,从而使得整个系统的运算速度大幅度提高。
    具体的操作步骤便为对照《哈工大停用词表》,去掉在每篇文献中出现频道都很高的词,如“一些”,“啊”,“的”等词语。之后再使用《NLPIR/ICTCLAS汉语分词系统2015版》,对我们的所有文摘进行切分词,进而得到所有出现过的词语的倒排档统计。并进一步利用倒排档统计进行接下来的工作,如tf*idf的计算。
    5.1.2 tf*idf通过前文阐述的中文切分词技术,我们已经实现了较为精准的匹配,保证用户输入检索词之后所发起的搜索得到的结果都是和用户实际需求有较高相关性的,接下来需要处理的便是排序的问题。
    比如用户输入“信息管理系统”,如何将相关度最高的图书排在最前列的位置,如何使得《管理信息系统》这本书的排序高于《信息管理系历史回顾》。这便是需要通过tf*idf来解决的问题。此外,比较两本书的相关程度,也是通过向量空间模型来计算tf*idf,从而发现不同图书之间的相关性,进而为读者做图书推荐。
    通过自动切分词结合通用词表,便可以把所有图书的文摘中出现的关键词提取,进而建立倒排档。而tf(I,j)是指关键词i在文献j中出现的次数,意即其词频。Idf则是分配给关键词的相对权重,其中常用词的idf值较低,而使用较少的词idf值较高。常用的idf公式为idf=log(N/n),其中N为总的文献篇数,n指某一词语在全部N篇文献中出现了n次。如果某个词语在全部N篇文章中都有出现,则其idf值等于log(1),等于0。而如果某个关键词则全部N篇文献中总共只出现在某一篇文献中,则其idf值等于log(N)。
    此外,我们还需要考虑摘要长度不同带来的问题,200字的摘要中和20字的摘要中,“信息”一词都出现了3次,显然“信息”在20字文摘中的重要程度更高。因此,我们需要将文摘长度归一化。在此处理的办法即为通过文摘中每个词语的绝对词频除以其文摘向量的长度。
    当以上三项准备就绪之后,我们便可以通过计算检索词与文摘之间的tf*idf值来进行检索结果的排序。若关键词与文摘的tf*idf结果为0,则统一不显示在检索结果中;其他情况下,则通过比较,将tf*idf值较高的摘要代表的图书排在靠前的位置,将tf*idf值较低的排在靠后的位置。
    并且,我们可以用向量空间模型来计算两篇文摘之间的相似度,若两篇文摘完全一样,则其相关度为1,若两篇文摘之间没有任何共同的关键词,则其相关性为0,而在为读者进行相关图书推荐时,我们便只需要将与用户借阅或收藏的图书的相关性较高的图书展现出来即可。
    5.2 通过图书找人的模式进行可视化通过图书找人的模式是我们图书馆系统的创新所在。传统的思维方式为通过读者的借阅历史记录分析读者感兴趣的话题内容,并根据向量空间模型为读者推荐与该书相似度较高的其他图书。
    我们的想法为:

    首先,通过数据可视化的方式来展现图书和读者之间的联系,运用到传统的推荐模式上,便是用线条粗细等方式反应读者和图书之间联系的强弱,图书与图书之间联系的强弱。这样读者便可以清楚明了的看到与自己联系较为密切的图书,以及与自己借阅的图书关系较为密切的其他图书
    其次,我们希望突破传统的方式,不仅是为读者推荐图书,而是与此同时做到为书推荐读者。文献领域的二八率表示80%的读者经常借阅的为整个文献资源20%的图书,而剩下的80%的图书则并不常用,很有可能只是被剩下的20%的读者借阅,因而面临着利用率低,甚至是资源浪费的问题。而我们通过帮图书推荐读者这一新颖的思维方式,便可以有效的辅助这一问题的解决

    而且,为书找合适的读者也是出版社和书商特别关注的话题。而如果能准备的为图书推荐合适的读者,对书商和出版社来说也很有可能增加其利润。比如,若借阅《白夜行》的读者有A,B,C,D等四名读者,而出版社即将出版与《白夜行》为同一作者,相似题材的《解忧杂货铺》。为了减小宣传成本,使得广告投放起到最大的效果,出版社通过借阅记录,发现A,B,C,D四名读者与和《解忧杂货铺》相似度很高的《白夜行》的联系非常密切,便将广告投放的重点放到这些读者上。
    以这样的方式来有效的节约成本,增加利润。提高文献资源的利用率采取的相似的方法:图书馆经常面临着图书利用率不足的问题,而且经常会面临图书利用率的审核。因此,以这样的方式来获取对新书可能感兴趣的潜在读者,便可以有效的提高图书馆文献资源的利用率。与此同时,还可以使读者阅读到自己感兴趣的图书,相当于一举两得。
    综上,我们将数据可视化与为图书推荐读者联结起来,用简单明了直接的方式显示图书与读者的联系,从而为书商、出版社和文化服务机构提供帮助,提高他们的利润或是绩效考核业绩。
    参考文献
    [1] 王珊,萨师煊,数据库系统概论第四版[M]. 北京,高等教育出版社,2006年.
    [2]孟楠. 分布式内存数据库系统设计实现与应用[D].南京理工大学,2005.
    [3]郭丽英. 高校图书馆数据库系统中SQL语句的查询重写研究[J]. 科技信息(学术研究),2008,04:220+223.
    [4]胡鹰. 中小型图书馆数据库管理系统的设计[J]. 图书馆,1989,02:33-36+17.
    [5]胡根桥. 图书馆流通数据仓库的设计与实现[J]. 现代情报,2007,07:68-70.
    0 评论 1 下载 2020-07-10 11:16:07 下载需要16点积分
  • 基于汇编语言的音乐盒设计与实现

    基于汇编语言的音乐盒设计与实现—汇编课设
    一 需求分析设计一个音乐盒,可用在诸如生日礼品等场景里。
    包含的功能有播放音乐、切换音乐。默认播放第一首音乐,单曲循环。当拨动控制开关时切换歌曲,总共三首,分别由三个开关控制。当且仅当一个开关开启其它开关关闭时有效,多个开关同时开启时无效。
    要求所选多个芯片或模块的加权值总和≥2,并且,所选芯片或模块中必须包含8259或8254芯片的其中一个作为功能模块部分。
    二 设计思路发声:通过将8254(8253)模块,设置为第三种模式,选择0号计数器,00110110B,作为波形发生器。通过输出波形的频率控制音调,通过维持频率波形的时间控制节拍。(1Mhz时钟信号下,频率与音阶对应如下:1=247,2=277,3=311,4=399,5=370,6=415,7=466, 1’=494)。
    控制:通过将8255模块,设置为方式0,A端口输入,10010000B,作为控制器。通过读取A端口输入的数据来选择相应的歌曲。每次读取到的数据存入寄存器,与下次读取到的做对比,来判断是否需要切换。切换时,不同开关位状态分别代表不同歌曲,设置的三首音乐分别对应001B,010B,100B。
    三 接线图设计8254:

    CLK0 => 1Mhz
    GATE0 => +5V
    OUT0 => 扬声器
    CS => 280H~287H

    8255:

    PA0~3 => SW0~3
    CS => 288H~28FH

    四 仿真设计使用Proteus实现仿真设计,设计图如下所示:


    五 程序设计5.1 程序流程图设计如下图所示:

    5.2 程序源代码 IOBASE EQU 280H IO8253_MODE EQU IOBASE+06H IO8253_0 EQU IOBASE+00H IO8255_MODE EQU IOBASE+0EH IO8255_A EQU IOBASE+08H PAGE 50,70 DATA1 SEGMENT FREQ1 DW 247,277,311,330,370,415,466,494,0 TIME1 DW 100,100,100,100,100,100,100,200,0 FREQ2 DW 311,311,277,311,311,370,311,277,311,247,247,277,311,370,310,277,277,247,277 DW 311,370,311,415,370,415,377,377,311,370 DW 311,277,311,370,311,277,277,247,0 TIME2 DW 100,50,50,200,50,50,50,50,200,100,50,50,50,50,100,100,50,50,200 DW 150,25,25,50,150,50,50,50,50,200 DW 100,50,50,100,50,50,50,200,0 FREQ3 DW 265,294,330,262,262,294,330,262,330,349 DW 392,330,349,392,392,440,392,349,330,262 DW 392,440,392,349,330,262,294,196,262,294 DW 196,262,0 TIME3 DW 50,50,100,100,100,100,100,50,50,100 DW 100,100,100,100,50,50,100,100,100,100 DW 100,100,50,50,100,100,100,100,100,50 DW 100,100,0,0 DATA1 ENDS STACK1 SEGMENT PARA STACK DW 100 DUP(?) STACK1 ENDS;==================== CODE SEGMENT ASSUME CS:CODE,DS:DATA1 ASSUME SS:STACK1 START: MOV AX,DATA1 MOV DS,AX MOV AX,0 MOV SI,AX MOV DX,IO8255_MODE MOV AL,90H ;A端口方式0输人 OUT DX,AL MOV DX,IO8253_MODE ;连接8253的控制端口 MOV AL,36H ;定义8253为通道0,方式3, OUT DX,AL ;二进制,先读低位/后读高位;;;MUSIC1=== MUSIC1: LEA DI,FREQ1 ;取偏移地址 LEA BP,TIME1 ;取时间偏移地址 LOOP1: MOV AX,[DI] ;取时间偏移地址 CMP AX,0 JE MUSIC1 CALL SPEAKER XOR AX,AX MOV DX,IO8255_A ;连接8255输入端口 IN AL,DX ;从手动控制端读入控制选择信息 MOV AH,0 CMP AX,SI ;判断输入的信息变化没有, JZ AAA1 ;没变则表示没有改变原来的选择 MOV SI,AX CMP AL,1H ;判断输入的信息 JNZ AAA2 ;选择播放那首音乐 JMP MUSIC1 AAA2: CMP AL,2H JNZ AAA3 JMP MUSIC2 AAA3: CMP AL,4H JNZ AAA1 JMP MUSIC3 AAA1: ADD DI,2 ADD BP,2 JMP LOOP1;;;===MUSIC1;;;MUSIC2=== MUSIC2: LEA DI,FREQ2 ;取偏移地址 LEA BP,TIME2 ;取时间偏移地址 LOOP2: MOV AX,[DI] CMP AX,0 JE MUSIC2 CALL SPEAKER XOR AX,AX MOV DX,IO8255_A ;连接8255输入端口 IN AL,DX ;从手动控制端读入控制选择信息 MOV AH,0H CMP AX,SI ;判断输入的信息变化没有,没变 JZ AAA6 ;则表示没有改变原来的选择 MOV SI,AX CMP AL,1H ;判断输入的信息,选择播放那首音乐 JNZ AAA4 JMP MUSIC1 AAA4: CMP AL,2H JNZ AAA5 JMP MUSIC2 AAA5: CMP AL,4H JNZ AAA6 JMP MUSIC3 AAA6: ADD DI,2 ADD BP,2 JMP LOOP2;;;===MUSIC2;;;MUSIC3=== MUSIC3: LEA DI,FREQ3 ;取偏移地址 LEA BP,TIME3 ;取时间偏移地址 LOOP3: MOV AX,[DI] CMP AX,0 JE MUSIC3 CALL SPEAKER XOR AX,AX MOV DX,IO8255_A ;连接8255输入端口 IN AL,DX ;从手动控制端读入控制选择信息 MOV AH,0H CMP AX,SI ;判断输入的信息变化没有,没 JZ AAA9 ; 变则表示没有改变原来的选择 MOV SI,AX CMP AL,1H ;判断输入的信息,选择播放那首音乐 JNZ AAA7 JMP MUSIC1 AAA7: CMP AL,2H JNZ AAA8 JMP MUSIC2 AAA8: CMP AL,4H JNZ AAA9 JMP MUSIC3 AAA9: ADD DI,2 ADD BP,2 JMP LOOP3;;;===MUSIC3;========播放音乐子程序======== SPEAKER PROC PUSH AX PUSH BX PUSH CX PUSH DX MOV AX,[DI] MOV BX,AX MOV DX,0FH MOV AX,4240H ;送入记数值clk=1MHZ 0x0F4240 = 1000000 DIV BX MOV DX,IO8253_0 ;将数送入8253计数器中 OUT DX,AL MOV AL,AH OUT DX,AL ;将每个音节的时间周期存入CX ;MOV CX,2710H ;设0.5s的时间周期所需的循环次数 MOV BX,WORD PTR DS:[BP] ADD BX,BX ADD BX,BX LOOP_TIMES: MOV AX,2710H ;设最小节拍播放时间为0.5s ADD AX,AX ADD AX,AX DELAY_TIME: DEC AX JNZ DELAY_TIME DEC BX JNZ LOOP_TIMES POP DX POP CX POP BX POP AX RET SPEAKER ENDP CODE ENDS END START六 实验总结该小程序锻炼了包括8086汇编语言的编写、各元器件微机接口的运用等。
    扬声器发出了音乐声,总体效果还行,第一首是声调测试,1234567;第二首是菊花台;第三首是两只老虎。其中,两只老虎的曲调是百度到的,实际播放节拍不对,推测是时钟信号频率不同导致的。
    3 评论 46 下载 2018-10-15 09:36:17 下载需要11点积分
  • 基于Python Flask框架和Mysql实现的二手物品交易平台

    1、需求分析1.1 系统目标二手物品发布平台是一个专为校内同学提供二手物品交易平台的系统。同学们可以首先注册账号,然后登录。登录之后可以在网站上浏览其他人或自己发布的二手物品及其相关信息,可以收藏自己喜欢的物品。用户也可以发布自己的二手物品。如果有感兴趣的物品,可以通过卖家留下的联系方式,线下通过第三方渠道进行支付、交易。
    该系统的目标是建立一个学校自己的二手物品交易平台,为需要出售二手闲置物品和需要购置二手物品的同学提供平台。
    1.2 系统功能需求该二手物品交易系统针对用户群体和系统管理员。
    用户的功能需求如下图所示:

    管理员的功能需求包括管理所有已发布的物品、管理所有用户发布的评论,如下图所示:

    1.3 系统的输入输出需求系统的输入应满足最小量原则、简单性原则以及及早检验原则,即在满足处理要求的前提下应使输入量尽量小,系统能够计算出的数据不要由用户输入;输入过程应尽量简单;对输入数据的检验尽量接近原始数据发生点,使错误能及时得到改正。
    系统的输出内容应简洁明了,使得用户一目了然;本系统中输出设备选择在屏幕上输出。
    1.4 系统的性能需求系统的性能需求主要表现在数据库中的各个表需要频繁地被插入、删除以及更新。对于用户来说,系统地相应时间不宜太长,否则会降低用户体验,为此需要建立良好的表结构,加上足够的存储空间以及硬件性能。
    2、可行性分析2.1 可行性研究前提随着互联网时代的飞速发展,人们的购物需求也在不断地增长。许多人也因此会产生许多闲置不用的物品,如何处理这些闲置的物品就成为一个难题。实地去二手市场进行买卖不失为一种选择,但是不符合互联网时代的“便捷”。在高校之中,学生们去二手市场进行交易显然是不可行的。因此,设计这样一个二手物品交易发布平台就显得十分必要。
    系统要求界面简洁直观,交互方便。能方便管理员管理用户们发布的二手物品交易和用户发表的评论,维护一个干净的平台。对于用户而言,浏览、搜索自己想要的二手物品是必要的功能(包括收藏自己喜爱的物品)。除此之外,用户也应该能够管理自己已经发布的二手物品,可以进行关闭交易、打开交易、撤销交易等操作。
    2.2 要求2.2.1 主要功能
    用户账户管理:用户可以在网站上自行注册及登录,离开时注销退出
    二手物品查询:用户以及管理员都可以在网站首页浏览所有用户发布的未关闭的二手物品,并且通过点击超链接进入查看详情
    用户评论管理:进入每一个已发布的未关闭的二手物品交易的详情页面之后,用户可以在下方发表评论。只有管理员可以管理删除这些评论
    二手物品管理:只有管理员可以管理所有人发布的二手物品交易,对于用户发布的不符合规范或者长期没有交易成功的物品撤销
    二手物品发布:所有非管理员用户均可发布二手物品,填写相应的资料和上传图片后即可发布
    搜索功能:可以根据物品名关键字进行搜索,搜索框中输入的字段必须完全包含在物品名称中

    2.2.2 安全性具有一定的安全性。系统中只有两级权限级别,即普通用户和系统管理员。普通用户的权限仅限于发布物品、管理自己发布的物品、发布评论,系统管理员的权限更大,能够管理所有普通用户发布的物品和所有用户发布的评论。由于权限等级的划分,系统的安全性、稳定性得到了一定地满足。
    2.2.3 性能可以方便快捷地完成二手物品查询,发布等各项操作。有一定的输入数据合法性检查功能,如用户发布物品时上传的物品只能属于规定的格式集合,用户注册时输入的邮箱地址的合法性。
    2.2.4 可扩展性能够适应需求的变化,开发新的功能。数据库表结构清晰,不产生冗余。
    3、数据库详细设计3.1 概念结构设计本二手物品交易平台中一共有用户、管理员、物品、评论这三个基本的模型(实体)。一个用户可以发布多个物品,而一个物品只能对应于一个用户。一个用户可以发布多条评论,而一条评论只能对应于一个用户。一个物品交易下可以有多条评论,但一条评论只能对应于一个物品。ER图如下:


    用户(非管理员):用户ID、用户名、用户密码、用户邮箱
    物品:物品ID、物品名称、物品价格、物品详细描述、物品图片
    评论:评论ID、评论内容、评论时间、评论所属物品、评论所属用户
    感兴趣:用户ID、物品ID

    管理员的属性和用户的属性基本相同,但管理员拥有管理所有发布的物品和发布的评论的功能,且管理员不能发布物品和评论。管理员是唯一的。
    3.2 逻辑结构设计将概念结构设计中的各个模型转化为DBMS支持的表结构,同时保持不会出现插入异常、删除异常和修改异常。表结构应该做到符合第三范式。在本系统中,一共有三个实体,实体转化为数据库模型为如下所示:

    实体转化为关系模式:

    用户(用户ID、用户名、用户密码、用户邮箱、用户权限)物品(物品ID、物品名称、物品价格、物品详细描述、物品图片)评论(评论ID、评论内容、评论时间、评论所属用户ID、评论所属物品ID)
    一个一对多联系转化为一个关系模式

    用户-物品(用户ID,物品ID)用户-评论(用户ID,评论ID)物品-评论(物品ID,评论ID)兴趣(用户ID、物品ID)

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



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




    Id
    integer
    4
    Not null
    用户ID(主键)


    Username
    varchar
    20
    Not null
    用户名


    Password
    varchar
    100
    Not null
    用户密码


    Email
    varchar
    30
    Not null
    用户邮箱


    Authority
    boolean
    1
    Not null
    用户权限



    物品表



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




    Id
    integer
    4
    Not null
    物品ID(主键)


    Name
    varchar
    30
    Not null
    物品名


    Description
    text
    不定
    Null
    物品详细描述


    Price
    flaot
    4
    Not null
    物品价格


    Imgpath
    varchar
    300
    Null
    物品图片URL


    Create_time
    datetime
    8
    Not null
    物品发布时间


    Is_closed
    integer
    4
    Not null
    交易是否被关闭


    Ownerid
    Integer
    4
    Not null
    所属用户ID



    评论表



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




    Id
    integer
    4
    Not null
    评论ID(主键)


    Content
    text
    不定
    Not null
    评论内容


    Itemid
    integer
    4
    Not null
    评论物品ID


    Ownerid
    integer
    4
    Not null
    评论所属用户ID


    Create_time
    datetime
    8
    Not null
    评论创建时间



    兴趣表



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




    Id
    integer
    4
    Not null
    兴趣ID(主键)


    Userid
    integer
    4
    Not null
    用户ID


    Itemid
    Integet
    4
    Not null
    物品ID



    其中除了兴趣之外的一对多关系模式在系统的具体实现中并没有建立实体的表结构,在使用flask框架中有相关的函数实现。
    3.3 物理结构设计3.3.1 选择关系模式的存取方式对数据库逻辑结构设计中建立的表结构,用户表的用户ID属性唯一决定每一个用户元组,所以对用户表建立以用户ID为主关键字的索引。同理,对物品关系模式、评论关系模式也采用类似的索引存取方法。
    3.3.2 数据表存储结构设计本系统的所有数据表均存放在物理磁盘中。用户表、物品表和评论表的结构是相对稳定的,表中的已有记录是要长期保存的,在此基础上系统会相应用户的操作对数据表进行增、删、查、改等操作。
    4、实现思路及相关技术4.1 开发条件4.1.1 开发语言系统使用的开发语言是Python。Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。Python 是由 Guido van Rossum 在八十年代末和九十年代初,在荷兰国家数学和计算机科学研究所设计出来的。Python 本身也是由诸多其他语言发展而来的,这包括 ABC、Modula-3、C、C++、Algol-68、SmallTalk、Unix shell 和其他的脚本语言等等。
    Python语言的特点有易于学习、易于阅读、易于维护、广泛的标准库和第三方模块、可移植性、可扩展性、提供同意的数据库接口API等等。正是因为Python语言拥有如此诸多的优秀特性,选择它作为开发二手物品交易平台能够使得整个开发、调试过程更加高效。
    4.1.2 开发框架本二手物品交易系统是基于Web的系统,以网站的形式呈现给用户。使用的Web开发框架是flask。Flask是一个使用Python编写的轻量级Web应用框架。其WSGI工具箱采用的是Werkzeug,模板引擎使用的是Jinja2。Flask也被称为 “microframework” ,因为它使用简单的核心,用 extension 增加其他功能。Flask没有默认使用的数据库、窗体验证工具。
    4.1.3 集成开发环境(IDE)编程所使用的集成开发环境是PyCharm,一款智能的Python编程集成开发环境。Pycharm拥有一般IDE具备的功能,如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测试、版本控制等。除此之外,Pycharm还提供了一些功能用于常用的Python的Web开发框架,如本系统用到的Flask框架,Pycharm就有非常友好的支持。
    4.1.4 数据库管理系统(DBMS)本系统使用的数据库管理系统是Mysql Community,版本号为5.7。MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于Oracle旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件。
    系统中的数据库以及数据库中的所有关系模式都使用Mysql进行管理。
    4.1.5 系统前端框架由于本系统是Web应用,所以使用了html+css+js的方式实现前端页面。实现过程中参考了Bootstrap前端开发框架。Bootstrap是Twitter退出的一个用于前端开发的开源工具包。在设计前端页面时,参考了Bootstrap的相关开源代码。
    4.2 系统界面设计用户登录界面

    用户注册界面

    首页

    物品详情页面

    发布物品页面

    用户个人中心页面之一

    用户个人中心页面之二

    修改个人资料

    管理员物品详情页面

    搜索物品栏

    登录用户及注销

    用户登录出错

    用户注册同名账号

    4.3 代码设计4.3.1 Flask项目配置使用Flask框架的flask_sqlalchemy插件配置连接数据库所需的参数,如使用的python数据库驱动、使用的DBMS、数据库名、端口号、用户名、密码等。除此之外,配置文件还包括用户登录之后session保存的时间长度、session加密密钥等。
    import osfrom datetime import timedeltaDEBUG = Truedialect = 'mysql'driver = 'pymysql'username = 'root'password = '68700695a'port = '3306'database = 'Q_A'SQLALCHEMY_DATABASE_URI = '{}+{}://{}:{}@localhost:{}/{}?charset=utf8'.format(dialect,driver,username,password,port,database)SQLALCHEMY_TRACK_MODIFICATIONS = FalseSECRET_KEY = os.urandom(24)PERMANENT_SESSION_LIFETIME = timedelta(days=7)
    4.3.2 用户登录、注册功能代码设计用户登录功能是通过读取用户在登录界面输入的账号和密码,在用户表中查找有无对应的项,如果有,则登录成功,跳转到首页,否则登录失败。
    ##登录@app.route('/login/',methods=['GET','POST'])def login(): if request.method == 'GET': return render_template('login.html',somethingwrong=False) else: username = request.form.get('username') password = request.form.get('password') user = User.query.filter(User.username==username).first() ##用户已经注册 if user and user.checkPassWord(password): session['user_id'] = user.id session.permanent = True return redirect(url_for('index')) else: return render_template('login.html',somethingwrong=True)
    代码中会判断当前访问URL的形式是get还是post,如果是get则渲染让用户填写登录信息的页面。如果是post则根据用户的输入,在用户表中首先查找是否有用户名匹配的项,如果有,则使用User模型的checkPassWord函数检查用户输入的密码通过同样的加密处理后是否于数据库中的项匹配,匹配则重定向至首页的URL。
    4.3.3 发布物品功能代码设计发布物品功能是通过读取用户在物品发布界面输入的物品名,物品详细描述,物品价格,以及上传的物品图片,创建一个新的物品对象,将其映射到实际的数据库中的物品表中。
    ##发布交易@app.route('/release/',methods=['GET','POST'])@login_requireddef release(): if request.method == 'GET': return render_template('realease_item.html') else: name = request.form.get('item_name') description = request.form.get('item_description') price = request.form.get('item_price') file = request.files.get('file') if file: img_name = file.filename if allowfiletype(img_name): imgurl = os.path.join(UPLOAD_PATH,img_name) file.save(imgurl) else: imgurl = None else: imgurl = None print(imgurl) item = Item(name=name,description=description,price=price,imgpath=imgurl) userid = session.get('user_id') item.owner = User.query.filter(User.id==userid).first() db.session.add(item) db.session.commit() return redirect(url_for('index'))
    图中的@login_required是python语言中的装饰器,它的作用是根据用户是否登录而返回不同的页面。当用户已经登录时:如果当前访问URL的形式是post形式,则根据用户提供的物品相关信息,创建一个新的物品对象,映射到物品表中。如果用户没有登录,则重定向至用户登录界面。
    4.3.4 搜索物品功能代码设计搜索物品功能是根据用户在搜索框中输入的关键字,在物品表中查找物品名称包含该关键字的项。然后在首页中按发布时间从最近发布的到最早发布的顺序列出。
    ##搜索@app.route('/search/')@login_requireddef search(): q = request.args.get('q') if q: items = Item.query.filter(Item.name.contains(q)).order_by('-create_time').all() else: items = Item.query.order_by('-create_time').all() return render_template('index.html',items=items)
    4.3.5 查看物品详情功能代码设计查看物品详情功能是通过点击网站首页的超链接进入的。代码如下图所示:
    ##物品详情及评论页面@app.route('/detail/<itemid>/')@login_requireddef detail(itemid): user = User.query.filter(User.id==session.get('user_id')).first() root_user = True if user.authority else False item = Item.query.filter(Item.id==itemid).first() if item.imgpath: item_img_name = item.imgpath.split('\\')[-1] else: item_img_name = '' imgpath = url_for('static',filename='images/{}'.format(item_img_name)) inter = Interest.query.filter(Interest.userid==session.get('user_id'),Interest.itemid==itemid).first() if inter: flag = True else: flag = False contents = { 'item_id':item.id, 'item_name':item.name, 'item_price':item.price, 'item_description':item.description, 'item_owner':item.owner.username, 'item_createtime':item.create_time, 'item_imgpath':imgpath, 'comments':item.comments, 'owner_email':item.owner.email, 'flag':flag, 'rootuser':root_user, } return render_template('item_detail.html',**contents)
    物品详情页面根据用户的权限而有所不同。首先根据用户登录时保存在session中的用户ID在用户表中查询,获取用户的权限。如果是管理员,则渲染出的额html页面中会有删除该物品和删除物品下方指定评论的功能按钮。如果是普通用户,则没有上述功能,只有查看物品相关信息,收藏按钮,发布评论等功能。
    4.3.6 在特定物品发布评论功能设计用户通过点击首页的物品名,转到物品详情页面之后,额可以在下方发布评论。然后点击提交,重新渲染物品详情页面,将新的评论映射到评论表中。
    ##在特定物品评论区添加评论@app.route('/add_comment/',methods=['POST'])@login_requireddef add_comment(): content = request.form.get('content') comment = Comment(content=content) ownerid = session.get('user_id') user = User.query.filter(User.id==ownerid).first() comment.owner = user itemid = request.form.get('item-id') item = Item.query.filter(Item.id==itemid).first() comment.item = item db.session.add(comment) db.session.commit() return redirect(url_for('detail',itemid=itemid))
    代码中的@login_required装饰器作用跟之前出现的作用相同。代码的功能是通过读取post请求提供的评论内容创建一个新的评论对象,然后根据当前登录用户的ID和当前所评论的物品的ID建立flask中的特殊关联对象relationship连接到用户表和物品表的对应项。
    4.3.7 个人中心功能代码设计点击导航栏右部的用户名,可以进入用户个人中心。个人中心的功能包括:查看用户已发布的物品,并能够开启/关闭交易、永久撤销交易;查看个人资料;查看用户收藏的感兴趣的物品;修改用户的个人资料。
    ##个人中心@app.route('/usercenter/<target>/')def usercenter(target): print(target) userid = session.get('user_id') user = User.query.filter(User.id==userid).first() items = user.items if target == 'items': print('target is items') return render_template('usercenter.html',type='item',items=items,flag=1) elif target=='profile': print('target is profile') ##找出用户所有感兴趣的物品 inters = Interest.query.filter(Interest.userid==userid).all() items_id = [] for inter in inters: items_id.append(inter.itemid) interested_items = [] for i in items_id: item = Item.query.filter(Item.id==i).first() interested_items.append(item) return render_template('usercenter.html',user_=user,interested_items=interested_items,type='profile',flag=2) elif target=='profile_edit': return render_template('usercenter.html',type='profile_edit',flag=3)
    图中的代码包含了给用户个人中心的三个分页面的实现。通过给定URL中的<target>的参数不同,渲染不同的页面返回给用户。
    4.3.8 关闭/开放物品交易功能代码设计关闭/开放某个物品交易功能的实现很简单,只需要根据物品ID在物品表中查找到对应的项,然后更改其isclosed属性即可。物品的isclosed属性更改以后,在网站首页上只会显示isclosed属性为0的那些物品。
    ##关闭某个交易@app.route('/closeitem/<itemid>/')def closeitem(itemid): item = Item.query.filter(Item.id==itemid).first() item.isclosed = 1 ##关闭该物品的交易 db.session.commit() return redirect(url_for('usercenter',target='items'))##开放某个交易@app.route('/openitem/<itemid>/')def openitem(itemid): item = Item.query.filter(Item.id==itemid).first() item.isclosed = 0 ##关闭该物品的交易 db.session.commit() return redirect(url_for('usercenter',target='items'))
    4.3.9 撤销物品交易功能代码设计撤销物品交易功能的实现比单纯的关闭物品交易复杂。在删除该物品交易之前首先需要删除该物品下所有的评论,然后再删除该物品。
    ##彻底删除某个交易(用户)@app.route('/deleteitem/<itemid>')def deleteitem(itemid): item = Item.query.filter(Item.id == itemid).first() comments = item.comments for comment in comments: db.session.delete(comment) db.session.delete(item) db.session.commit() return redirect(url_for('usercenter',target='items'))
    4.3.10 管理员删除二手物品、评论功能代码设计当登录用户具有管理员管理员时,在点击进入物品详情页面之后,可以选择删除当前的物品,也可以选择删除指定的评论。
    ##删除某个发布的物品(管理员)@app.route('/deleteitem_root/<itemid>/')def deleteitem_root(itemid): item = Item.query.filter(Item.id==itemid).first() comments = item.comments for comment in comments: db.session.delete(comment) db.session.delete(item) db.session.commit() return redirect(url_for('index'))##删除评论(管理员)@app.route('/deletecomment/<commentid>/')def deletecomment(commentid): comment = Comment.query.filter(Comment.id==commentid).first() itemid = comment.itemid db.session.delete(comment) db.session.commit() return redirect(url_for('detail',itemid=itemid))
    两个函数分别对应删除物品和删除评论。在删除物品函数中,首先进行的是删除该物品下的所有评论,然后再删除物品,返回网站首页。在删除指定评论的函数中,根据评论ID删除评论表中的相应项即可,返回当前页面。
    4.3.11 用户收藏感兴趣物品功能代码设计用户登录以后,点击进入物品详情页面之后如果感兴趣可以点击感兴趣按钮收藏该物品。收藏之后,可以在用户个人中心中查看自己的收藏列表。如果对该物品不再感兴趣,点击不感兴趣按钮即可。
    ##对某个物品感兴趣@app.route('/interest/<itemid>/')def interest(itemid): userid = session.get('user_id') new_interest = Interest(userid=userid,itemid=itemid) db.session.add(new_interest) db.session.commit() return redirect(url_for('detail',itemid=itemid))##取消对某个物品的感兴趣@app.route('/de_interest/<itemid>/')def de_interest(itemid): userid = session.get('user_id') inter = Interest.query.filter(Interest.userid==userid,Interest.itemid==itemid).first() db.session.delete(inter) db.session.commit() return redirect(url_for('detail',itemid=itemid))
    4.3.12 用户修改个人资料功能代码设计用户可以在个人中心修改自己的资料,比如修改密码和邮箱地址。一次修改过程必须填写密码和邮箱,如果某项不需要更改,填写原来的内容即可。
    ##修改个人资料@app.route('/edit_profile/',methods=['POST'])def edit_profile(): userid = session.get('user_id') user = User.query.filter(User.id==userid).first() new_email = request.form.get('new_email') new_password1 = request.form.get('new_password1') new_password2 = request.form.get('new_password2') if new_password1 != new_password2: return render_template('usercenter.html',type='profile_edit',flag=3,password_wrong=True) user.email = new_email user.password = generate_password_hash(new_password1) db.session.commit() return redirect(url_for('usercenter',target='profile'))
    5、实现要点难点5.1 前端页面设计本系统的Web前端页面的所有界面都有相同的部分,如页面顶部的导航栏。

    大致上系统的的每一个功能模块都有对应着一个html页面,如果所有的html页面都要手动加上导航栏的相关代码,则实现起来将会十分繁杂、麻烦。为了解决这个共享的“模板”问题,采用的技术是flask框架的jinja2引擎所提供的父模板抽离功能。将需要共享的html代码段放在一个html文件中作为父模板,然后在需要使用模板的页面的html文件中使用{%extends ‘base.html’%}的形式继承父模板即可。父模板中可以用{%block xxx%}{%endblock%}的形式来提供给子页面来填充不同的内容,在子页面中在{%block xxx%}{%endblock%}中填入需要的html代码即可。
    在页面中需要根据从数据库中查询到的数据进行内容填充的部分的实现是通过flask框架的Jinja2引擎中的变量、条件语句、循环语句、过滤器等功能来实现的。在Jinja2中,变量通过下图的形式引入:
    {{item_name}}
    条件语句通过下图的形式引入:
    {% if rootuser==False %}{% endif %}
    循环语句通过下图的形式引入:
    {% for item in items %}{% endfor %}
    有了Jinja2引擎的丰富功能,就可以在视图函数中根据传递的参数的不同渲染不同的页面。
    5.2 python语言与数据库的交互在DBMS中使用SQL语句与数据库进行交互是自然而又方便的。但是在使用python进行实现的时候如何与数据库(mysql)进行交互就是一个必须解决的问题。第一种解决方案是使用python的mysql驱动,然后创建游标,将sql语句放在python的字符串中,然后执行sql语句。这种方法可以完成Python与数据库的交互,但是太过底层。Flask框架提供了一套ORM框架可以让我们方便地进行与数据库的交互。ORM,全称是Object Relational Mapping,即对象关系映射。对象关系映射是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上来说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。
    在Flask框架中,通过使用flask_sqlalchemy插件,让python的class机继承自SqlAlchemy()对象的Model,然后在class的定义中创建一系列类变量和数据库中的列一一对应。
    #二手物品模型class Item(db.Model): __tablename__ = 'item' id = db.Column(db.Integer,primary_key=True,autoincrement=True) ##物品ID name = db.Column(db.String(30),nullable=False) ##物品名称 description = db.Column(db.Text,nullable=True) ##物品详细描述 price = db.Column(db.Float,nullable=False) ##物品价格 imgpath = db.Column(db.String(300),nullable=True) ##物品图片url ownerid = db.Column(db.Integer,db.ForeignKey('user.id')) ##物品发布者id owner = db.relationship('User',backref=db.backref('items')) ##物品发布者 create_time = db.Column(db.DateTime,default=datetime.now) ##物品发布时间 isclosed = db.Column(db.Integer,nullable=False,default=0) ##物品交易是否被关闭,1为关闭
    Flask_sqlalchemy插件中包含有sql中各种数据类型对应的对象,以及一系列约束条件,如外键约束等等。除了这些基本的属性对应之外,flask_alchemy还提供了十分方便的relation关系,可以快速实现一对多模型的建立。
    模型和表的映射建立完成之后,需要考虑的是如何将对模型对象的增、删、查、改等操作映射到数据库中。Flask_sqlalchemy插件也提供了这些操作的python实现。增加数据和删除数据是通过db.add函数和db.delete函数实现,查询是通过继承自db.Model的类对象的filter函数进行筛选的,修改数据是通过直接在筛选得到的类对象修改相应属性实现的。
    添加
    new_user = User(username=username,email=email,password=password1,authority=rootuser)db.session.add(new_user)
    删除
    db.session.delete(item)
    筛选
    user = User.query.filter(User.id==userid).first()
    修改
    user.email = new_emailuser.password = generate_password_hash(new_password1)
    所有的操作需要进行事务提交才能映射到数据库中生效:
    db.session.commit()
    6、总结这次数据库课程大作业是一次非常有趣的实践过程。通过把老师在课上讲的内容运用到实战中去,不仅能够提高对数据库的理解,也同时锻炼了我们关于WEB开发相关的技能,如html、css、javascript等代码的编写能力。除此之外也让我更加深入地了解了Python语言的flask框架,实际运用了ORM模型来实现操作类对象和数据库交互。
    设计一个二手物品发布平台需要进行需求分析,可行性分析等一系列研究。分析用户在使用我的系统时需要哪些功能,怎样设计WEB页面能够让用户简单快捷地进行想要的操作。在数据库中建立所需要的数据表,并且最大限度地保证没有冗余,且查询等操作的速度较快。有一点经验是在编写代码的过程中需求可能是变化的,比如某个时刻可能需要给一个表添加新的属性,那么通过flask_script和flask_manage插件可以完成数据库的migrate和upgrade,更新实际表的内容。
    系统实现的功能包括了基本需求,注册、登录、发布、评论、个人中心等等。但没有实现类似淘宝、京东那样的购物网站一样的交易功能,本系统的交易功能是以提供卖家的联系方式让用户和卖家通过第三方渠道交易实现的,相当于提供了一个类似预定的功能。
    3 评论 28 下载 2019-10-30 09:05:07 下载需要15点积分
  • 基于JAVA的迷宫游戏设计与实现

    一、问题描述1.1 课程设计题目程序开始运行时显示一个迷宫地图,迷宫中央有一只老鼠,迷宫的右下方有一个粮仓。游戏的任务是使用键盘上的方向键操纵老鼠在规定的时间内走到粮仓处。
    1.2 课程设计要求
    老鼠形象可辨认,可用键盘操纵老鼠上下左右移动
    迷宫的墙足够结实,老鼠不能穿墙而过
    正确检测结果,若老鼠在规定时间内走到粮仓处,提示成功,否则提示失败
    添加编辑迷宫功能,可修改当前迷宫,修改内容:墙变路、路变墙
    找出走出迷宫的所有路径,以及最短路径
    利用序列化功能实现迷宫地图文件的存盘和读出等功能

    二、需求分析迷宫游戏,作为一种经典的游戏,与之相关的程序可谓琳琅满目,数不胜数。在这种情况下,自己设计的程序要想有点看头,单靠黑洞洞的控制台是绝对不行的,至少要有像样一点的图形界面和简单方便的操作。
    上学期刚学过C++之后,我通过自学MFC,完成了拥有图形界面的课程设计。这学期学数据结构的同时,也学习了另外一门重要的高级语言——java,所以,也想牛刀小试,玩一下java的图形界面设计。
    为了达到题目中的要求,用java实现的话,显然需要用到它的图形界面、事件驱动、多线程等知识。前两部分内容,教材上讲的已基本上能帮我完成相关设计了,多线程的知识,教材上没有,但查阅相关资料(包括图书馆的纸质书籍和网上的各种论坛信息),也很容易找到设置时间限制的方法。
    Java工具的问题解决了,毕竟大神们把这些工具设计的很方便和漂亮,使用起来当然不会太难,所以,剩下的才是核心部分——真正实现程序设计要求的重要算法。
    用户只能自己设计迷宫玩是不太方便的,当然也不能没有这个功能,但我想在保留此功能的同时,添加一个生成随机迷宫的功能。当然这种随机不能是简单的调用java提供的random方法,这样所得到的迷宫很难保证有通路。要想设计出随机的,而且至少有一条通路的迷宫,自然要用到特殊的、巧妙的算法。另外,要求出最短路径,也是需要好的算法设计的,算法时间复杂度太高的话,用户也会等的不耐烦的。
    三、概要设计3.1 程序界面设计程序主要有两个界面,均继承自javax.swing.Jframe,它们分别是开始界面StartUI和游戏界面map。
    StartUI界面提供4个操作按钮,让用户选择游戏的场地大小,其中有一个按钮允许用户自定义场地大小。其实,这种设计完全是在模仿linux系统gnome集成桌面环境中所提供的扫雷游戏。
    map界面主要是迷宫地图和操作菜单栏,在此选择了使用动态的gif图片显示老鼠。另外,除了实现利用方向键控制老鼠外,也顺便为菜单选项设计了快捷键(反正用的是相同的事件驱动处理程序,多加几个按键响应而已)。
    3.2 总体功能框架程序总体功能大致可分为游戏模块、编辑模块、文件操作模块以及提示路径模块,另外在菜单栏中提供一些帮助信息,姑且把它看作是帮助模块吧。
    结合程序界面,程序总体功能框架如下图所示:

    程序最核心,最重要的算法主要用在游戏模块和路径提示模块中,其它模块只不过是一些简单的操作而已。
    四、详细设计4.1 数据结构设计程序源码由四个源文件组成,每个源文件有一个属性为public的类,它们分别是StartUI、map 、Operations、wrmPane。其中,StartUI和map是程序界面类,wrmPane是用于显示老鼠、路、墙、粮仓等元素的面板,Operations类中几乎提供了对迷宫进行操作的所有方法。此外,在某些类的定义里又有匿名内部类的存在,不再细述。
    虽然本程序的核心算法用到了栈和图论的思想,但我并没有定义相应的数据结构。其原因在于:对于栈来说,本来就可以通过对数组的简单操作很方便的实现压栈、出栈等功能;对于图来说,由于迷宫可以看作是一个相对来说很有特点的图,它点与点之间的关系不像一般的图那样随意,一个点只可能与它周围四个方向有确定关系的点之间具有直接联系。因此,迷宫本身自带的信息就已经构成了一种抽象一点的邻接链表或邻接矩阵(我本人感觉它更像是个邻接链表),所以也没必要再去实际构造这些数据类型来存储迷宫信息。
    为了降低求取迷宫最短路径算法的时间和空间复杂度,也就是说尽可能地优化算法,程序设计中引入了路径深度和路径深度图的概念,从而很巧妙地设计出了相对于传统的迷宫最短路径算法更易理解且效率更高的算法(当然,这种绝妙的算法思想绝不是我的小脑袋瓜所能构想出来的,这是我从一篇论文上看到的)。明白了它的思想之后,算法是很容易写出来的。这种算法虽然离不开图的思想,但还是能通过对数组的简单操作来实现。
    顺便说一下,迷宫地图的每一块最小单元,其实就是二维平面上的一个点,所以,它本来是应该以二维的坐标来标识的,不过,我设计代码总喜欢尽可能地把维数降到最低,最后在算法设计中还是把它们转化为与之一一对应的点了。其实,这次的降维对提高算法效率而言,并没有什么实质性的效果,只是能让我写代码的时候看着更舒服而已(可能我确实有点强迫症吧)。
    4.2 具体模块设计下面介绍一下各功能模块的设计。由于各功能模块的实现难易程度不同,那些简单的模块就不再详细说明了。
    4.2.1 游戏模块该模块用到了一个重要的算法,同时需要对设计关于时间控制的线程以及按键事件驱动的处理。其中使用到的最主要的方法,原型如下:
    public static void DFS(int s); public static void creatMaze();
    creatMaze方法通过调用DFS方法实现至少有一条通道的随机迷宫的生成。至于控制时间的线程,不过是继承Runnable接口后重写一下run方法进行倒计时,按键事件处理不过是根据按键不同调用相应方法,限于篇幅,它们的具体实现不再赘述。下面主要讲述一下生成随机迷宫的算法思想,并以流程图的方式大致描述一下它的代码实现。
    该算法利用了图的深度优先遍历,算法的最初设计仅适用于行数和列数均为奇数的随机迷宫生成,我做了一点修改以使它能避开行数和列数的限制,同时将老鼠的位置设为随机。算法思想不难理解,我将修改后的算法思想大致描述如下:
    首先,将所有的点初始化为墙,然后随机生成老鼠的位置,再将相应点修改成老鼠,粮仓的位置修改好;然后,从老鼠位置出发,利用深度优先遍历算法,通过随机遍历方向,将所有与老鼠位置X、Y坐标差值均为偶数的点遍历出来,并修改成路,最终连接成一条能联通所有这些点的通道。在此过程中,当我们需要由一个点连接到下一个点时,仅需要打通隔开它们的那堵墙(将它修改成路)即可。
    遍历结束后,仅能保证老鼠可以到达迷宫上与之位置坐标差值均为偶数的任意点,而对于行数和列数均为奇数,且入口和出口分别位于左上角和右下角的迷宫来说,其入口和出口恰好符合这种关系,但条件不全就不能确保迷宫有通路了,这正是原算法的缺陷所在。弥补这个缺陷的方法很简单,我借助下/图说明一下:

    假设粮仓在图2中的位置3上,很显然,田字格中必然有一个点与老鼠位置满足上述关系,也就是说,老鼠能到达该点。我们可以看到,若老鼠能到达2或4点的话,它也就能到达3了,所以唯一的问题就是位置1,我们不难发现,联通位置1和3也很简单,只需打通2或4就行了。于是,每次遍历后随机将位置2或4修改为路,就能保证迷宫在任何情况下都有通路了。
    其实,还有一个不太重要的小问题,对于不满足行列数均为奇数的迷宫,这种算法产生的随机迷宫会在某一个或两个全为墙的边,这样的迷宫看起来是有点不“协调”的,所以,我稍微修改了一下遍历前的初始化工作,让这些边上的点随机初始化为墙或路。
    通过以上步骤,就使得生成的迷宫满足随机性的同时,也至少拥有一条通道。
    下面是算法大致流程图。
    creatMaze方法流程图

    DFS方法流程图

    4.2.2 文件操作模块该模块利用文件选择器java.awt. JFileChooser.。以二进制文档方式保存或导入迷宫结构。内容比较简单,故不再详述。
    4.2.3 编辑模块编辑模块利用ActionListener接口,通过对鼠标点击事件的处理,提供了墙路互变的功能,其中对生成的随机迷宫的编辑同样调用了creatMaze方法。
    4.2.4 路径提示模块该模块在程序非常重要,包括有程序两个核心算法,而且还引入了关于图论的新概念。模块中用到的两个方法,原型如下:
    public static void sortestPath();public static void findPath();
    在讲述sortestPath方法之前先引入以下两个概念:
    路径深度——从某位置走到出口的最短路径的长度,设每一方块为单位路径长度。假定出口处路径深度为0 , 障碍处路径深度为 - 1 。
    路径深度图——与迷宫对应的图, 每一个节点值为与该节点对应的迷宫单元格的路径深度。
    基于上述概念,不难证明,迷宫最短路径必然是一条从入口处开始、到出口处结束的路径深度逐次递减1的序列。因此,如果能求出整个迷宫每个单元格的路径深度,那么求解迷宫最短路径就简单了。于是,问题的重点就在于如何求解路径深度图。
    稍作思考,我们不难理解,对于迷宫上任意一个非障碍物的点,它的路径深度值必然不大于其周围四个方向上的非障碍物点中最小路径深度值+1。sortestPath方法就是在这个结论的基础下设计的算法。算法每一个循环动态地更新所有点的路径深度直至整张图达到稳态。再通过得到的路径深度图求得最短路径。该方法的流程图如下:

    findPath方法使用传统的回溯法查找迷宫中的一条接近最短路径的通道,我感觉它有点贪心法的影子,因为总是先挑眼前看来是最容易接近出口的方向,但其实质还是深度优先搜索,只是在搜索过程中设置了四个方向的优先级。
    回溯法算法思想很简单:从当前点出发时,访问的下一个点要选择未访问过的可行方向中优先级最高的。若当前点周围所有方向上的点都不能访问,便往后退一步,继续以相同方式探路。如此循环往复,直至找到迷宫出口或完全无路可走。显然这里需要用到栈存储路径,并通过压栈和出栈操作实现探路过程中的前进和后退。
    由于回溯法是一个经典算法,有关它的资料很多,在这里就不再画它的流程图了,但这并不代表我认为它不重要,它仍然是本程序设计中的三大核心算法之一。
    4.2.5 帮助模块该模块利用消息框显示游戏使用说明和作者信息,没什么可细述的。
    五、调试运行分析5.1 经典迷宫最短路径算法与本程序新算法在时间和空间效率上的对比本程序求解最短路径的算法借助了路径深度图的思想,算法完成的工作可以分为两部分,即求解路径深度图和通过路径深度图求解最短路径。
    显然算法的主要时间开销在第一部分上,这一部分主要过程是逐步判断每个单元格是否与周围四个单元格达到稳定平衡状态,设迷宫规模为 M*N,则时间复杂度为O(4*M*N),空间方面仅需一个长度为 M*N 的数组来存储各单元格的路径深度。
    第二部分中当最短路径长度为K时,时间方面最多需要4*K次,空间方面存储路径只需长度为K队列。而K=O(M+N),故这一部分时间空间复杂度均为O(M+N)。
    综上所述,本算法的时间和空间复杂度均为O(M*N)。
    而经典迷宫最短路径算法中广度优先搜索,空间复杂度方面除了要存储迷宫单元格访问信息外,还需使用堆栈来保存大量搜索记录。时间复杂度方面和具体迷宫布局有关, 主要时间开销在探索上, 由于算法采用了递归反复探索, 通常情况下访问一个单元格后要依次访问 4 个后继方向上的单元格, 第一次找到迷宫出口的路径即为最短路径。假设最短路径长度要 N+M, 当不设置迷宫单元格访问矩阵时, 则几乎要探索 O(4^(N+M) ) 次, 时间复杂度远大于本算法。
    深度优先算法, 空间复杂度与深度优先搜索相差不大, 但时间复杂度方面, 算法要实现找出最短路径, 必须遍历出所有到达出口的路径, 比较各条路径的总长度, 从而得出最短路径,因此时间复杂度大于广度优先搜索, 自然而然远大于本算法。
    由此,可得出结论,本程序使用的新算法明显优于传统算法。
    5.2 程序运行效果演示在Windows控制台或linux终端输入指令java StartUI,即进入程序开始界面:

    可选择程序提供的迷宫场地大小,也可以自定义迷宫行数和列数:

    当输入数据有误或点击取消时,会有错误提示:

    以后程序使用过程中,用户的误操作都会有相应提示,不再提供相关截图。
    选择好正确的迷宫场地大小后,即可进入游戏界面:

    刚开始,游戏是没有时间限制的,当在游戏菜单中选择设置时间限制选项时,弹出输入对话框:

    真确设置好时间后,弹出如下消息:

    选择之后,发现上方开始倒计时:

    现在可以开始玩游戏,通过方向键控制老鼠的移动
    当剩余时间不多时,倒计时颜色改变:

    在时间限制内是否完成任务,都会有相应提示:

    在提示菜单中有显示最短路径和随意显示一个路径的子菜单,点击后会有相应路径的显示:

    文件菜单的子菜单中可选择保存迷宫结构或导入迷宫结构:


    编辑模式中,可点击某块区域使墙路互变,不再截屏。
    4 评论 235 下载 2018-10-31 10:54:10 下载需要13点积分
  • 基于Android和SSM框架实现的学生成绩管理系统APP

    1、项目概述1.1 项目的目的和意义建立学生成绩管理系统,利用手机对学生成绩进行管理,进一步提高现代化水平。使学生能够快速、准确地输入、修改和查询自己的成绩,更好地了解自己的学业成绩,并方便对教师的管理。让学生更快地查询自己的学业成绩。教师可以更快更好地了解学生的学习情况,从而进一步提高学校的教学质量。
    1.2 现状分析学校对学生成绩的管理曾经还停留在使用手工操作上。随着学校规模的扩大,学生人数逐年增加。随着学生成绩管理数据量的不断增加,一些学校不得不依靠不断增加的人力物力来进行学生成绩管理。但是,人工管理存在效率低、易出错、信息检索速度慢、难以为学校管理提供决策信息等缺点。学校不能充分掌握每个学生的学习情况,这不利于学校的教学工作,学校的教学质量一直停滞不前,所以我只想开发一个适用的绩效管理体系,使学生能够充分掌握学业的成绩和课程,以便于学生了解情况。
    2、主要业务流程分析成绩管理系统可以输入课程和成绩数据,并可以完成添加、修改、删除等查询,并根据各种条件基本满足师生的需要。成绩管理系统主要包括学生的基本信息、年龄、电话等方面的信息,内容比较简单。成绩管理系统主要用于学生成绩信息的输入,具有课程注册、成绩查询、学生信息注册等功能。一般来说,它具有编辑、查询、学生成绩管理等功能。
    学生端登陆该系统后,进入个人信息页面可以查看自己的个人信息,进入课程管理页面可以对已选课程的课程成绩查询,显示其学号、科目名称和成绩,进入课程论坛页面可以进入现有话题查看和跟帖,也可以创建新的讨论话题。
    教师端登录系统后可以进入个人信息页面可以查看自己的个人信息,还可以先选择已开放课程,之后进行成绩录入、成绩修改、成绩查询、成绩分析、删除课程的操作,也可以增加新开放的课程。成绩录入先选择需要录入的人数,再添加其学号与成绩。成绩修改先输入需要修改的成绩的学生学号进行查询,然后输入其姓名,科目,任课教师,成绩,然后提交修改。成绩查询可查询已选此门课程的学生的学号、姓名和成绩。成绩分析可以选择分数比例分析图查看饼状统计图,或者选择分段人数分析图查看条状统计图。删除课程就会删除此门开放课程。
    3、需求分析3.1 功能需求
    成绩管理系统可以提供一个完善的登陆界面,使学生或教师都可以通过此窗口登陆
    成绩管理系统可以提供完善的学生信息和课程信息管理界面,学生或管理员可以查看和管理学生信息与课程信息
    成绩管理系统可以提供完善的课程信息管理界面,教师或管理员可以查看和管理课程信息,进行成绩录入、成绩修改、成绩查询、成绩分析
    成绩管理系统有一个后端管理页面,可以作为管理员登陆查看与修改学生信息,教师信息,课程信息和论坛信息

    3.2 非功能需求
    登陆时采用的用户名 和密码都采用MD5算法保护,再存入数据库存储
    该系统能够在android4.0.13以上版本的安卓系统上正常运行

    4、系统功能设计
    5、系统UI设计




























    6、系统数据库设计/*服务器hostname: www.wuyunian.tk密码: wuyunian1998学号:8位数是老师,10位数是本科生*//*学生学号 密码 姓名 入学年份 手机号 */ drop table if exists `student_id`;CREATE TABLE IF NOT EXISTS `student_info`( `id` VARCHAR(100) NOT NULL, `password` VARCHAR(100) NOT NULL, `name` VARCHAR(100) NOT NULL, `year` int not null, `phonenum` varchar(11) not null, PRIMARY KEY ( `id` ))ENGINE=InnoDB DEFAULT CHARSET=utf8;/*老师职工号 密码 姓名 */drop table if exists `teacher_info`;CREATE TABLE IF NOT EXISTS `teacher_info`( `id` VARCHAR(100) NOT NULL, `password` VARCHAR(100) NOT NULL, `name` VARCHAR(100) NOT NULL, `phonenum` varchar(11) not null, PRIMARY KEY ( `id` ))ENGINE=InnoDB DEFAULT CHARSET=utf8;/*课程课程编号 课程名称 任课教师职工号 学生学号 学生成绩*/drop table if exists `subject`;CREATE TABLE IF NOT EXISTS `subject`( `subject_id` VARCHAR(100) NOT NULL, `subject_name` VARCHAR(100) NOT NULL, `teacher_id` VARCHAR(100) NOT NULL, `student_id` VARCHAR(100) , `score` double)ENGINE=InnoDB DEFAULT CHARSET=utf8;/*讨论话题讨论话题编号 讨论的话题 教师职工号 学生学号 老师发言 学生发言*/drop table if exists `discussion`;CREATE TABLE IF NOT EXISTS `discussion`( `discussion_id` int NOT NULL auto_increment, `topic` VARCHAR(100) NOT NULL, `name` VARCHAR(100) NOT NULL, `teacher_id` VARCHAR(100) , `student_id` VARCHAR(100) , `teacher_words` VARCHAR(200) , `student_words` VARCHAR(200) , PRIMARY KEY ( `discussion_id` ))ENGINE=InnoDB DEFAULT CHARSET=utf8;/*插入学生数据 */INSERT INTO student_info (id, password, name,year,phonenum) VALUES("1163710214", "123456", "Anderson",2018,18846135525);INSERT INTO student_info (id, password, name,year,phonenum) VALUES("1163710215", "123456", "刘文佳",2018,15512931688);INSERT INTO student_info (id, password, name,year,phonenum) VALUES("1152625346", "123456", "佳佳大魔王",2018,15512931688);INSERT INTO student_info (id, password, name,year,phonenum) VALUES("1554651223", "123456", "克格勃佳佳",1976,15512931688); /*插入老师数据*/insert into teacher_info(id,password,name,phonenum)values("20181234","123456","奥巴马","18846135525"); insert into teacher_info(id,password,name,phonenum)values("19982356","123456","普京","11913582155"); /*插入课程数据*/insert into subject(subject_id,subject_name,teacher_id,student_id,score)values("cs001","Data constructure And Algorithm","20181234","1163710214",90.6);insert into subject(subject_id,subject_name,teacher_id,student_id,score)values("cs001","Data constructure And Algorithm","20181234","1163710214",90.6);insert into subject(subject_id,subject_name,teacher_id,student_id,score)values("cs002","Software Constructure","19982356","1163710214",85.9);insert into subject(subject_id,subject_name,teacher_id,student_id,score)values("math001","gaoshu","19982356","1163710215",85.9);/*插入讨论数据*/insert into discussion(topic,name,teacher_id,student_id,teacher_words,student_words)values("工业互联网","基于工业互联网的感想","20181234","1163710214","我觉得不行","我觉得ok");insert into discussion(topic,name,teacher_id,student_id,teacher_words,student_words)values("测试大数据导论","基于hadoop进行数据分析","20181234","1163710214","我觉得不行","我觉得ok");insert into discussion(topic,name,teacher_id,student_id,teacher_words,student_words)values("人工智能","价值一亿美元的人工智能代码竟然是用java写的","19982356","1163710214","我觉得不行","我觉得ok");insert into discussion(topic,name,teacher_id,student_id,teacher_words,student_words)values("人工智能","AI","19982356","1163710214","我觉得不行","我觉得ok");
    主要的表

    各个表的结构




    7、系统体系结构设计后端采用SSM框架,采用spring+springmvc+mybatis的技术方案,实现后端快速开发;前端基于Navigation Drawer Activity和LoginActivity两个基础Activity,使用sharedpreferences,HTTP,本地化存储等技术进行开发。

    8、统一接口设计


    序号
    url
    方式
    返回值
    返回格式
    备注




    1
    http://localhost:8080/HITManage/login?id=1163710215&password=123456
    post
    true or false
    json
    登录验证


    2
    http://localhost:8080/HITManage/reqstumsg?id=1163710215
    get
    student_info
    json
    请求学生信息


    3
    http://localhost:8080/HITManage/reqteamsg?id=20160101
    get
    teacher_info
    json
    请求老师信息


    4
    http://localhost:8080/HITManage/reqallsubject
    get
    Arraylist<String>
    json
    请求所有课程名称


    5
    http://localhost:8080/HITManage/reqsubject?subjectname=gaoshu&student_id=1163710215
    get
    subject
    json
    请求某学生某课程的课程信息


    6
    http://localhost:8080/HITManage/reqsubscore?id=1
    get
    Arraylist<Subject_info>
    json
    请求某课程所有的成绩


    7
    http://localhost:8080/HITManage/reqalldis
    get
    Arraylist<String>
    json
    请求所有讨论名称


    8
    http://localhost:8080/HITManage/reqdis?disname=
    get
    Arraylist<discussion>
    json
    请求某讨论所有内容


    9
    http://localhost:8080/HITManage/addStudent?id=116&password&name&year&phonenum
    post
    ?
    ?
    添加学生账号


    10
    http://localhost:8080/HITManage/addScore
    post
    格式:Arraylist<subject>
    json
    添加学生成绩


    11
    http://localhost:8080/HITManage/addDiscussion
    post
    格式:Arraylist<discussion>
    json
    添加讨论信息


    12
    http://localhost:8080/HITManage/reqSubid?subname=gaoshu
    get
    string
    json
    请求某课程的编号


    13
    http://localhost:8080/HITManage/reqSubByTeaId?teacherid=1163710215
    get

    json



    14
    http://localhost:8080/HITManage/newDiscussion?topic=人工智能&name=啥玩意啊
    post
    true or false
    json
    添加讨论圈子


    14
    http://localhost:8080/HITManage/addDiscussion?
    post
    discussion
    json
    添加某话题下的讨论内容


    15
    http://localhost:8080/HITManage/delDiscussion?
    post
    diccussion
    json
    删除某条讨论内容(先不实现)


    16
    http://localhost:8080/HITManage/reqSubByStuId?studentid=1163710215
    get
    status boolean data List<Subject>
    json
    请求某学生的课程信息


    17
    http://localhost:8080/HITManage/modifyPassword?id=1163710215&password=123456
    post
    staues true
    json
    修改密码



    9、其它需要说明的设计要点中文编码问题:由于在前后端传输数据时,若传输中文数据容易变为乱码,因此在前后端进行中文传输时,函数要在开始前进行编码统一。

    10、详细设计与成果先介绍android前端的设计思路,首先设计的是login活动,该活动登陆时读取用户输入的信息,在本地进行加密,加密后送至服务器进行匹配,并返回是否允许登陆的信息。再次登陆时利用sharedpreference存储历史登陆信息。第二个是注册活动,该活动还允许学生注册新的账户与密码,发送至服务器并保存。第三个是修改密码活动,输入用户名,新密码旧密码,以发送至服务器并修改。第四个是显示主页面的活动,通过侧边栏接入更多的功能。第五个是个人信息活动,也是通过请求后端数据库以活动该用户的信息以显示。第六个是学生端的课程信息活动,该活动可以查询自己所有已选课程的成绩,需要通过添加适配器动态地显示多条信息。第七个是教师端的课程信息活动,教师可以增删查改各门课程的成绩,同样是请求后端对数据库进行增删查改,教师端还有一个成绩分析的活动,该活动根据对后端请求所得的特定课程的所有成绩信息进行分段,同样该活动也使用了适配器,然后分别用饼状图和柱状图具体的展示给教师。第八个是论坛讨论功能,先使用一个适配器展示所有的讨论话题,点击一个话题后利用第二个适配器展示所有该话题下的所有讨论内容。
    然后是web管理段的设计思路,该页面相当于将所有存在数据的内容,包括学生信息,教师信息,课程信息,论坛信息,一并的显示于页面上,并且可以自由的增删查改,该页面当然也与后端数据库连接。
    后端采用ssm框架。在后端中有简单的页面:老师界面、学生界面、讨论区界面、课程界面,每个界面实现了基本的增删改查功能。
    在框架中,也贯穿了软件设计过程中的MVC思想。model体现在pojo这个包中,这个包实现了讨论、老师、学生、课程这几个基本类。views体现在web界面的jsp中。control体现在controller这个包中。
    spring利用控制反转和依赖注入,将对象之间的依赖关系交由spring进行控制,避免硬编码所造成的过度程序耦合。
    springMVC实现了利用注解简化了配置,实现可视化。
    mybatis与数据库进行连接,将数据库操作放在xml文件中,避免出现在java代码中,极大地降低了数据库操作语言的书写过程和连接等过程。
    在后端接受来自前端jsp或者android传来的get或者post请求,对请求中的内容进行解析,如json数据、String类型等,调用数据库操作,最后将得到的结果、状态信息等封装到json中返回给请求方。
    11、项目体会了解到了如何将SSM框架应用于后端的开发,在与他人合作编写代码时,要提前商量并确定下来一定的书写规范,可以保证代码的整洁度,也可以为后续的整合以及修改工作提供便利。在编写后端逻辑功能时,要时刻关注前端到后端再到数据库的数值传输,以此来确保模块功能的实现,同时减少代码编写过程中产生的bug。简而言之,代码的书写思路必须要清晰。在android 最初学习阶段,通常会遇到很多问题,Andorid 学习过程中知识点繁多,难以熟练的应用,不经过很长一段时间很难以熟练地运用自己所学的知识。其实Andorid 并不是很难,无非就是布局,Activity 中获取布局,对布局进行传值操作。并且学习Android 开发 fragment ViewPager 适配器是必不可少的,在任意一款app 中都有所应用。
    8 评论 105 下载 2019-07-09 15:16:31 下载需要13点积分
  • 基于JSP的SSM框架实现的员工信息管理系统

    这是完整使用SSM框架开发的第一个项目,项目来源于北京动力节点的SSM框架整合教程,其中加入了一些自己的理解,增加了一个搜索功能的页面,这个项目总体来说对于新手是很友好的,涉及到了简单的ajax和jquery处理,UI搭建,后端SSM环境搭建,简单的业务流程设计(使用JSON交互)。
    使用SSM框架搭建的一个简单的员工信息管理系统,实现了基本的增删改查整个流程。
    直接把war包放到tomcat的webapps目录下,重启即可运行。
    一、系统架构
    二、功能分解
    查询和分页显示
    新增员工信息功能

    数据校验,需要校验用户名和邮箱是否合法前端使用jquery校验,后端使用JSR303
    修改员工信息功能
    单个删除员工
    批量删除员工
    使用Rest风格的URI
    搜索功能

    三、技术点
    基础框架:SpringMVC+Spring+MyBatis
    数据库:MySQL
    前端框架:BootStrap快速搭建
    项目依赖管理:Maven
    分页:pagehelper(Mybatis的工具)
    逆向工程:MyBatis Generator

    四、数据库表设计
    五、功能实现5.1 查询和分页显示5.1.1 数据分析分析前端UI,分析要从数据库获取的信息

    修改Mapper映射文件

    5.1.2 业务逻辑分析
    访问index.jsp页面
    发送ajax请求获取员工数据
    服务器返回JSON数据
    浏览器解析JSON数据,使用DOM增删页面

    5.1.3 实现
    导入jackson包,把对象转换成JSON字符串,在controller中添加ResponseBody注解即可
    定义一个新的实体类Msg.java

    3个属性:
    响应码(100成功 200失败)提示信息(msg)返回给浏览器的数据(Map<String,Object>封装)
    3个方法:
    静态Success:返回Msg对象,设置响应吗100,提示信息处理成功静态Fail:返回Msg对象,设置响应吗200,提示信息处理失败Add方法:用于把pageInfo放入到map中public Msg add(String key, Object value) { this.getExtend().put(key,value); return this;}


    index.jsp使用Jquery进行拼接元素,显示表格

    5.1.4 流程图
    5.2 新增员工信息功能5.2.1 业务逻辑
    index.jsp页面点击新增
    弹出新增员工的对话框(模态框)
    数据库查询部门列表,显示在对话框中
    用户输入数据

    校验数据
    完成保存

    5.2.2 数据分析
    姓名和邮箱要做前端+后端校验
    部门根据数据库信息返回的添加进去


    查出部门信息

    前端校验用户名和邮箱是否合法

    后端校验用户名和邮箱是否合法

    使用的是JSR303校验
    当输入框输入完成移开鼠标之后,发送AJAX到服务器请求用户名是否已经存在,并且是否是合法的用户名
    如果校验不通过,那么保存信息的按钮应处于不可用状态


    保存新增的员工信息
    调用的是Jquery的serialize()方法,把表单数据包装成对象

    5.3 修改员工信息功能5.3.1 逻辑分析
    点击修改按钮,发送AJAX信息,请求当前修改员工的数据库信息
    弹出模态框,回显员工信息,姓名不允许修改
    校验邮箱

    注意
    由于修改和删除按钮都是页面加载完成后,由AJAX发送请求,再添加到页面的,所有click事件不生效,所以要使用dom对象操作

    TOMCAT直接使用PUT请求的时候不会封装请求体数据,要使用SpringMVC的过滤器完成PUT

    5.3.2 数据分析
    5.3.3 业务实现
    获取部门信息增加到下拉框
    获取员工信息添加到模态框
    点击保存按钮更新员工信息到数据库


    5.4 删除员工5.4.1 单个删除
    5.4.2 批量删除
    添加选择按钮
    实现批量删除

    添加选择按钮

    实现批量删除

    5.5 搜索功能5.5.1 逻辑
    增加一个高级搜索的按钮,跳转到高级搜索的界面query.jsp
    后台需要判断输入条件

    5.5.2 数据分析
    5.5.3 实现表现层
    /*** 查询功能的查询员工信息* 查询出来的员工数据显示在一页上面*/@RequestMapping(value="/queryEmps",method=RequestMethod.POST)@ResponseBodypublic Msg queryEmp(Employee employee) { System.out.println(employee); List<Employee> emplist = employeeService.queryEmp(employee); return Msg.success().add("emplist",emplist);}
    Service层
    public List<Employee> queryEmp(Employee employee) { // TODO Auto-generated method stub EmployeeExample example = new EmployeeExample(); Criteria criteria = example.createCriteria(); System.out.println("".equals(employee.getEmpName())); if (!("".equals(employee.getEmpName()))) { criteria.andEmpNameEqualTo(employee.getEmpName()); } if(!("".equals(employee.getEmail()))) { criteria.andEmailEqualTo(employee.getEmail()); } criteria.andGenderEqualTo(employee.getGender()); criteria.andDIdEqualTo(employee.getdId()); return employeeMapper.selectByExampleWithDept(example);}
    4 评论 140 下载 2019-03-06 18:51:03 下载需要13点积分
  • 基于Huffman哈夫曼编码的文件压缩与解压缩

    一、实验题目用哈夫曼编码实现文件压缩
    二、实验目的
    了解文件的概念
    掌握线性链表的插入、删除等算法
    掌握Huffman树的概念及构造方法
    掌握二叉树的存储结构及遍历算法
    利用Huffman树及Huffman编码,掌握实现文件压缩的一般原理

    三、实验设备与环境
    微型计算机
    Windows 系列操作系统
    Visual C++6.0软件

    四、实验内容根据ascii码文件中各ascii字符出现的频率情况创建Haffman树,再将各字符对应的哈夫曼编码写入文件中,实现文件压缩。
    五、概要设计5.1 构造Hufffman树的方法—Hufffman算法构造Huffman树步骤:

    根据给定的n个权值{w1,w2,……wn},构造n棵只有根结点的二叉树,令起权值为wj
    在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和
    在森林中删除这两棵树,同时将新得到的二叉树加入森林中
    重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树

    5.2 Huffman编码:数据通信用的二进制编码
    思想:根据字符出现频率编码,使电文总长最短
    编码:根据字符出现频率构造Huffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列

    5.3 压缩压缩流程图

    5.4 解压根据存放在文件中的编码表和文件压缩后的编码,进行一对一的翻译过程。
    六、测试结果及分析压缩文件

    压缩前文件的内容

    压缩后文件的内容

    解压文件

    解压后文件的内容
    2 评论 109 下载 2019-06-09 16:16:35 下载需要12点积分
  • 基于Java的迷宫游戏

    一 需求分析系统需实现以下功能:

    迷宫的随机生成

    能随机生成单元格像素宽度为10,迷宫行列长度为40*40的迷宫
    寻找路径

    能寻找从起点到终点的路径
    遍历迷宫

    能走遍迷宫内的所有点获得迷宫地图
    清空迷宫

    能清除已经生成的迷宫,以便再次生成不同的随机迷宫
    动态实现走迷宫的过程

    即运用动画效果,用一个小球模拟电脑鼠在计算机上展示出走迷宫的过程

    二 程序设计2.1 总体设计系统总体结构如下图:

    2.2 关键算法设计2.2.1 生成迷宫的算法算法思路
    指定40*40大小的迷宫,从迷宫的(0,0)点开始访问,访问四个方向中的随机一个点(每访问到一个可访问的点,就去掉该点的那个方向的墙),被访问点继续以这种方识向下进行访问。对每个被访问的点都被标识为已访问,当一个点对某个方向进行访问时我们首先会判断被访问点是否已被访问,或者触到边界.如果该点四个方向皆已访问或已无法访问,就退回上一个点,上一个点继续这个过程。
    算法实现
    主要是利用栈,第一次,先把第一个点压进栈里,每访问到一个点,就把该点压进栈里,再对栈顶的点进行四个方向的随机访问,访问到新点,又把新点压进去,一旦这个点四个方向都无法访问了,就让该点退栈,再对栈顶的点的四个方向进行访问,以此类推,直到栈里的点都全部退出了,迷宫成功生成了。
    算法流程图如下:

    2.2.2 寻找路径的算法算法思路
    从迷宫的第一个单元格开始,随机访问它相邻的四个单元格的其中一个格子,判断他们之间是否有通路,有通路则继续以这种方式往下访问,若无通路或者四个单元格都已经访问,则返回上一个单元格继续访问,直至访问到迷宫中的最后一个单元格。
    算法实现
    主要也是利用栈。将第一个单元格的行列坐标压入栈里,随机访问一个它相邻的四个单元格的一个,将其标记为已访问并将其坐标压入栈里,判断该单元格的坐标是否是终点坐标,若是,则停止访问,若不是,则判断该单元格与上一个单元格之间是否有通路,若有,则继续往下访问,若没有,则将该单元格的坐标从栈顶删除,并返回上一个单元格重新访问,直至访问至终点坐标,这样栈里面就存储了从起点到终点的可行路径。在访问时,利用右手法则进行访问。
    算法流程图如下:

    2.2.3 遍历迷宫的算法算法思路
    随机访问迷宫里的一个单元格,并标记为已访问,然后随机访问该单元格相邻的四个单元格,并标记为已访问。判断已访问的单元格数是否等于全部单元格的个数,若两者相等,则停止访问,遍历完成,若不相等,则继续往下访问。若正访问的单元格相邻四个单元格均已访问过,则随机从所有单元格随机再取出一个单元格继续访问,以此类推,直到所有单元格都被访问过为止。
    算法实现
    主要利用栈。从所有单元格里随机取出一个单元格,标记为已访问,将其坐标压入栈里,接着随机访问一个相邻的单元格,并标记为已访问,将其坐标压入栈里。此时判断栈的长度是否等于等于0,若相等,则停止访问,遍历完成,若不相等,继续往下访问。若正访问的单元格相邻四个单元格均已访问过,则删除栈顶单元格的坐标,然后随机从所有单元格随机再取出一个单元格继续访问,以此类推,直到所有单元格都被访问过为止。采用左手法则进行遍历。
    算法流程图如下:

    三 程序实现3.1 关键类与方法类之间的协作关系如下图所示:

    3.1.1 UI类主要用于显示界面和完成各个功能。
    包含的方法如下:

    Start(StageprimaryStage):重写start面板creatXY():绘制X,Y坐标creatButton():控制按钮的生成paintMaze():绘制出迷宫paintCell(Stringstring):绘制单元格的墙壁creatLine(intx1,y1,x2,y2):绘制单元格的一面墙paintball():绘制小球Mazefile():随机生成迷宫的数据文件,将其写入磁盘PaintMoveBall1(inti,int j,int m,int n):绘制寻找路径时小球的行走路线PaintMoveBall2(inti,int j,int m,int n):绘制遍历迷宫时小球的行走路线InputMaze():将迷宫地图导入栈中moveBall():控制小球移动travelMaze():遍历迷宫时控制动画的函数deleteMzae():清除迷宫exit():用于退出程序findWay():用于寻找路径的函数travel():用于遍历迷宫的函数
    package io.github.xieyezi;import java.io.File;import java.io.FileNotFoundException;import java.io.PrintWriter;import java.util.ArrayList;import java.util.Random;import java.util.Scanner;import javafx.animation.KeyFrame;import javafx.animation.Timeline;import javafx.application.Application;import javafx.geometry.Insets;import javafx.geometry.Pos;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.layout.BorderPane;import javafx.scene.layout.Pane;import javafx.scene.layout.VBox;import javafx.scene.paint.Color;import javafx.scene.shape.Circle;import javafx.scene.shape.Line;import javafx.scene.text.Text;import javafx.stage.Stage;import javafx.util.Duration;public class UI extends Application { private int countLine; // 绘制迷宫时控制跨行 private int currentX; // 绘制迷宫时定位X坐标 private int currentY; // 绘制迷宫时定位Y坐标 private int ballCol = 0;// 小球当前所在列 private int ballRaw = 0;// 小球当前所在行 private final int LENGTH = 10; // 迷宫一个单元格一面墙的长度 private Timeline animation; // 显示小球路径的动画 Pane pane = new Pane();// 用来装迷宫的面板 BorderPane borderpane1 = new BorderPane(); // 用来装迷宫的pane,用于清除迷宫 BorderPane borderpane = new BorderPane(); // 主面板 Circle ball = new Circle(55, 55, 5);// 模拟电脑鼠运动的小球 Button bt1 = new Button("生成迷宫"); Button bt2 = new Button("寻找路径"); Button bt3 = new Button("遍历迷宫"); Button bt4 = new Button("最短路径"); Button bt5 = new Button("清空迷宫"); Button bt6 = new Button("退出游戏"); VBox vbox = new VBox(bt1, bt2, bt3, bt4, bt5, bt6); cell[][] cell = new cell[40][40];// 存储小球在迷宫的行列位置 myStack stack = new myStack();// 存储运动位置的栈 // 主函数 public static void main(String[] args) { Application.launch(args); } // 重写start显示面板 @Override public void start(Stage primaryStage) throws Exception { creatXY(); // 绘制坐标轴 createButton(); // 生成控制按钮 vbox.setSpacing(20); vbox.setAlignment(Pos.CENTER); borderpane.setRight(vbox); //borderpane1.setBottom(getHBOox()); borderpane1.setCenter(pane); borderpane.setCenter(borderpane1); borderpane.setPadding(new Insets(0, 30, 12, 0)); Scene scene = new Scene(borderpane, 600, 500); primaryStage.setTitle("MouseMaze"); primaryStage.setScene(scene); primaryStage.show(); } // 绘制坐标轴函数 private void creatXY() { Line l1 = new Line(50, 50, 450, 50);// X轴 l1.setStrokeWidth(2); Line l2 = new Line(50, 50, 50, 450);// Y轴 l2.setStrokeWidth(2); Text t1 = new Text(40, 40, "(50,50)"); Text t2 = new Text(440, 40, "X (450,50)"); Text t3 = new Text(35, 450, "Y"); Text t4 = new Text(35, 465, "(50,450)"); borderpane.getChildren().addAll(l1, l2, t1, t2, t3, t4); } // 控制按钮生成 private void createButton() { bt1.setMinSize(80, 40);// 定义按钮的大小 bt1.setStyle("-fx-border-color: blue");// 定义按钮边框的颜色 bt2.setMinSize(80, 40); bt2.setStyle("-fx-border-color: blue"); bt3.setMinSize(80, 40); bt3.setStyle("-fx-border-color: blue"); bt4.setMinSize(80, 40); bt4.setStyle("-fx-border-color: blue"); bt5.setMinSize(80, 40); bt5.setStyle("-fx-border-color: blue"); bt6.setMinSize(80, 40); bt6.setStyle("-fx-border-color: blue"); // 按钮的功能的生成 bt1.setOnAction(e -> paintMaze()); bt2.setOnAction(e -> { animation = new Timeline(new KeyFrame(Duration.millis(5), e1 -> moveBall())); animation.setCycleCount(Timeline.INDEFINITE); animation.play(); }); bt3.setOnAction(e -> travelMzae()); bt4.setOnAction(e -> samllWay()); bt5.setOnAction(e -> deleteMaze()); bt6.setOnAction(e -> exit()); } // 主面板的底部界面// private HBox getHBOox() {// HBox hbox = new HBox();// // 以下四个文本域分别表示为起点的x,y坐标和终点的x,y坐标// TextField startX = new TextField();// TextField startY = new TextField();// TextField endX = new TextField();// TextField endY = new TextField();// startX.setPrefSize(40, 10);// startY.setPrefSize(40, 10);// endX.setPrefSize(40, 10);// endY.setPrefSize(40, 0);// Label l1 = new Label("起点坐标:");// Label l2 = new Label("终点坐标:");// l1.setTextFill(Color.RED);// l2.setTextFill(Color.RED);// l1.setFont(Font.font("Times New Roman", FontWeight.BOLD, 18));// l2.setFont(Font.font("Times New Roman", FontWeight.BOLD, 18));// hbox.getChildren().addAll(l1, startX, startY, l2, endX, endY);// hbox.setAlignment(Pos.TOP_CENTER);// hbox.setSpacing(5);// return hbox;// } // 绘制迷宫 private void paintMaze() { try { Mazefile(); // 随机生成迷宫数据文件并将其存入磁盘 } catch (Exception e1) { e1.printStackTrace(); } paintBall(); // 生成模拟电脑鼠的小球 countLine = 0; // 控制换行 currentX = 50; // 迷宫的起点坐标的x坐标 currentY = 50; // 迷宫的起点坐标的y坐标 File file = new File("Maze.txt"); Scanner input = null; try { input = new Scanner(file); while (input.hasNext()) { String str = input.next(); paintCell(str); // 绘制墙壁 } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { input.close(); } inputMaze();// 获取迷宫数据存入Cell } // 绘制墙壁 private void paintCell(String str) { if (str.charAt(0) == '0') createLine(currentX, currentY, currentX + LENGTH, currentY);// 绘制单元格上边的墙 if (str.charAt(1) == '0') createLine(currentX + LENGTH, currentY, currentX + LENGTH, currentY + LENGTH);// 绘制单元格右边的墙 if (str.charAt(2) == '0') createLine(currentX, currentY + LENGTH, currentX + LENGTH, currentY + LENGTH);// 绘制单元格下边的墙 if (str.charAt(3) == '0') createLine(currentX, currentY, currentX, currentY + LENGTH); // 绘制单元格左边的墙 currentX += LENGTH; // 绘制下一个单元格 countLine++; if (countLine % 40 == 0)// 控制绘制下一行 { currentX = 50; currentY += LENGTH; } } // 绘制单元格的一面墙壁 private void createLine(int x1, int y1, int x2, int y2) { Line line = new Line(x1, y1, x2, y2); line.setStrokeWidth(2); line.setFill(Color.BLACK); pane.getChildren().add(line); } // 绘制小球 private void paintBall() { ball.setCenterX(55); ball.setCenterY(55); ball.setRadius(5); ball.setStroke(null); ball.setFill(Color.RED); pane.getChildren().add(ball); } // 随机生成迷宫数据文件函数 ,“0”表示有墙,“1”表示无墙 public void Mazefile() throws Exception { // 生成迷宫数据 存储到pointList链表里面 File file = new File("Maze.txt"); PrintWriter output = new PrintWriter(file); Maze maze = new Maze(); ArrayList<mazePoint> pointList = new ArrayList<mazePoint>(); pointList = maze.getMaze(); int i = 0; // 定义随机数的一个对象 Random random = new Random(); while (i < pointList.size()) { int l = random.nextInt(4); if ((i + 1) % 8 != 0 && (i + 1) % 7 != 0 && i > 7 && i < 56) { if (pointList.get(i).getDirection() < 3) { if (l == 0 && pointList.get(i + 1).getDirection() >= 1) { pointList.get(i).setRight(1); pointList.get(i + 1).setLeft(1); } else if (l == 1 && pointList.get(i + 8).getDirection() >= 1) { pointList.get(i).setDown(1); pointList.get(i + 8).setUp(1); } else if (l == 2 && pointList.get(i - 1).getDirection() >= 1) { pointList.get(i).setLeft(1); pointList.get(i - 1).setRight(1); } else if (l == 3 && pointList.get(i - 8).getDirection() >= 1) { pointList.get(i).setUp(1); pointList.get(i - 8).setDown(1); } } } // 将数据存储到文件里面 output.print(pointList.get(i).getUp()); output.print(pointList.get(i).getRight()); output.print(pointList.get(i).getDown()); output.print(pointList.get(i).getLeft()); output.print(' '); i++; if (i % 8 == 0) { output.println(); } } output.close(); } // 绘制寻找路径的行走路线 private void paintMoveBall1(double i, double j, double m, double n) { Line line = new Line(i, j, m, n); line.setStrokeWidth(2.5); line.setFill(Color.GREENYELLOW); line.setStroke(Color.RED); pane.getChildren().add(line); } // 绘制遍历的行走路线 private void paintMoveBall2(double i, double j, double m, double n) { Line line = new Line(i, j, m, n); line.setStrokeWidth(2.5); line.setFill(Color.GREENYELLOW); line.setStroke(Color.BLUE); pane.getChildren().add(line); } //绘制最短路径的行走路径 private void paintMoveBall3(double i, double j, double m, double n){ Line line = new Line(i, j, m, n); line.setStrokeWidth(4); line.setFill(Color.GREENYELLOW); line.setStroke(Color.CHARTREUSE); pane.getChildren().add(line); } // 迷宫地图数据导入栈 private void inputMaze() { File file = new File("Maze.txt"); Scanner input = null; while (!stack.empty()) { stack.pop(); } stack.push(cell[ballCol][ballRaw]);// 将起点压入栈 try { int i = 0; int j = 0; int n = 0; input = new Scanner(file); while (input.hasNext()) { String str = input.next(); char a1 = str.charAt(0); int a = a1 - '0'; char b1 = str.charAt(1); int b = b1 - '0'; char c1 = str.charAt(2); int c = c1 - '0'; char d1 = str.charAt(3); int d = d1 - '0'; cell[i][j] = new cell(a, b, c, d);// 将迷宫地图数据存储到Cell[][] cell[i][j].setDix(n); n++; i++; if (i % 40 == 0) { i = 0; j++; } } } catch (Exception e) { e.printStackTrace(); } finally { input.close(); } } // 控制小球移动 private void moveBall() { // 优先顺序,右左下上 if (ballCol == 39 && ballRaw == 39) { animation.pause(); } else if (stack.getSize() >= 0) { findWay(); } } // 遍历迷宫时控制动画的函数 private void travelMzae() { animation = new Timeline(new KeyFrame(Duration.millis(5), e1 -> travel())); animation.setCycleCount(Timeline.INDEFINITE); animation.play(); } // 寻找最短路径时控制动画的函数 private void samllWay() { animation = new Timeline(new KeyFrame(Duration.millis(10), e1 -> smallestWay())); animation.setCycleCount(Timeline.INDEFINITE); animation.play(); } // 清空迷宫函数 private void deleteMaze() { animation = null; pane.getChildren().clear(); ballCol = 0; // 将行重新初始化 ballRaw = 0; // 将列重新初始化 while (!stack.empty()) { stack.pop(); } } // 退出函数 public void exit() { System.exit(0); } //寻找路径 private void findWay() { // 利用右手法则来寻找路径 cell[ballCol][ballRaw].setVisted(true); if (cell[ballCol][ballRaw].getRight() == 1 && cell[ballCol + 1][ballRaw].getLeft() == 1 && cell[ballCol + 1][ballRaw].isVisted() == false && cell[ballCol + 1][ballRaw].isPass() == true) {// 右转 stack.push(cell[ballCol][ballRaw]); cell[ballCol + 1][ballRaw].setVisted(true); double i = ball.getCenterX(); double j = ball.getCenterY(); ballCol++; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); double m = ball.getCenterX(); double n = ball.getCenterY(); paintMoveBall1(i, j, m, n); } else if (ballCol > 0 && cell[ballCol][ballRaw].getLeft() == 1 && cell[ballCol - 1][ballRaw].getRight() == 1 && cell[ballCol - 1][ballRaw].isVisted() == false && cell[ballCol - 1][ballRaw].isPass() == true) {// 左转 stack.push(cell[ballCol][ballRaw]); cell[ballCol - 1][ballRaw].setVisted(true); double i = ball.getCenterX(); double j = ball.getCenterY(); ballCol--; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); double m = ball.getCenterX(); double n = ball.getCenterY(); paintMoveBall1(i, j, m, n); } else if (cell[ballCol][ballRaw].getDown() == 1 && cell[ballCol][ballRaw + 1].getUp() == 1 && cell[ballCol][ballRaw + 1].isVisted() == false && cell[ballCol][ballRaw + 1].isPass() == true) {// 下转 stack.push(cell[ballCol][ballRaw]); cell[ballCol][ballRaw + 1].setVisted(true); double i = ball.getCenterX(); double j = ball.getCenterY(); ballRaw++; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); double m = ball.getCenterX(); double n = ball.getCenterY(); paintMoveBall1(i, j, m, n); } else if (ballRaw > 0 && cell[ballCol][ballRaw].getUp() == 1 && cell[ballCol][ballRaw - 1].getDown() == 1 && cell[ballCol][ballRaw - 1].isVisted() == false && cell[ballCol][ballRaw - 1].isPass() == true) {// 上转 stack.push(cell[ballCol][ballRaw]); cell[ballCol][ballRaw - 1].setVisted(true); double i = ball.getCenterX(); double j = ball.getCenterY(); ballRaw--; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); double m = ball.getCenterX(); double n = ball.getCenterY(); paintMoveBall1(i, j, m, n); } else { // 若四面都有墙,则删除栈顶的坐标,回到上一个访问的坐标 cell cell1 = new cell(); if (stack.getSize() > 0) { cell1 = stack.pop(); for (int i = 0; i < 40; i++) for (int j = 0; j < 40; j++) { if (cell1.compareTo(cell[i][j]) == 0) { if (j > 1 && cell[i][j].getDir() == 0 && cell[i][j].getDirection() <= 1) { cell[i][j - 1].setPass(); } else if (i < 39 && cell[i][j].getDir() == 1 && cell[i][j].getDirection() <= 1) { cell[i + 1][j].setPass(); } else if (j < 39 && cell[i][j].getDir() == 2 && cell[i][j].getDirection() <= 1) { cell[i][j + 1].setPass(); } else if (i > 0 && cell[i][j].getDir() == 3 && cell[i][j].getDirection() <= 1) { cell[i - 1][j].setPass(); } ballCol = i; ballRaw = j; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); break; } } } } } //遍历迷宫 private void travel() { boolean visitedAll = true; for (int i = 0; i < cell.length; i++) { for (int j = 0; j < cell[i].length; j++) { if (cell[i][j].isVisted() == false) visitedAll = false; } } if (ballCol == 0 && ballRaw == 0 && visitedAll == true) { animation.stop(); while (!stack.empty()) { stack.pop(); } } else { //利用左手法则来遍历迷宫 if (ballCol > 0 && cell[ballCol][ballRaw].getLeft() == 1 && cell[ballCol - 1][ballRaw].getRight() == 1 && cell[ballCol - 1][ballRaw].isVisted() == false && cell[ballCol - 1][ballRaw].isPass() == true) { stack.push(cell[ballCol][ballRaw]); cell[ballCol - 1][ballRaw].setVisted(true); double i = ball.getCenterX(); double j = ball.getCenterY(); ballCol--; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); double m = ball.getCenterX(); double n = ball.getCenterY(); paintMoveBall2(i, j, m, n); } else if (cell[ballCol][ballRaw].getRight() == 1 && cell[ballCol + 1][ballRaw].getLeft() == 1 && cell[ballCol + 1][ballRaw].isVisted() == false && cell[ballCol + 1][ballRaw].isPass() == true) { stack.push(cell[ballCol][ballRaw]); cell[ballCol + 1][ballRaw].setVisted(true); double i = ball.getCenterX(); double j = ball.getCenterY(); ballCol++; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); double m = ball.getCenterX(); double n = ball.getCenterY(); paintMoveBall2(i, j, m, n); } else if (ballRaw > 0 && cell[ballCol][ballRaw].getUp() == 1 && cell[ballCol][ballRaw - 1].getDown() == 1 && cell[ballCol][ballRaw - 1].isVisted() == false && cell[ballCol][ballRaw - 1].isPass() == true) { stack.push(cell[ballCol][ballRaw]); cell[ballCol][ballRaw - 1].setVisted(true); double i = ball.getCenterX(); double j = ball.getCenterY(); ballRaw--; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); double m = ball.getCenterX(); double n = ball.getCenterY(); paintMoveBall2(i, j, m, n); } else if (cell[ballCol][ballRaw].getDown() == 1 && cell[ballCol][ballRaw + 1].getUp() == 1 && cell[ballCol][ballRaw + 1].isVisted() == false && cell[ballCol][ballRaw + 1].isPass() == true) { stack.push(cell[ballCol][ballRaw]); cell[ballCol][ballRaw + 1].setVisted(true); double i = ball.getCenterX(); double j = ball.getCenterY(); ballRaw++; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); double m = ball.getCenterX(); double n = ball.getCenterY(); paintMoveBall2(i, j, m, n); } else { cell cell1 = new cell(); if (stack.getSize() > 0) { cell1 = stack.pop(); for (int i = 0; i < 40; i++) for (int j = 0; j < 40; j++) { if (cell1.compareTo(cell[i][j]) == 0) { if (j > 1 && cell[i][j].getDir() == 0 && cell[i][j].getDirection() <= 1) { cell[i][j - 1].setPass(); } else if (i < 39 && cell[i][j].getDir() == 1 && cell[i][j].getDirection() <= 1) { cell[i + 1][j].setPass(); } else if (j < 39 && cell[i][j].getDir() == 2 && cell[i][j].getDirection() <= 1) { cell[i][j + 1].setPass(); } else if (i > 0 && cell[i][j].getDir() == 3 && cell[i][j].getDirection() <= 1) { cell[i - 1][j].setPass(); } ballCol = i; ballRaw = j; ball.setCenterX(55 + ballCol * LENGTH); ball.setCenterY(55 + ballRaw * LENGTH); break; } } } } } } //最短路径 private void smallestWay(){ if(stack.empty()){ findWay(); } for(int i = 0;i<stack.getSize();i++){ cell cell1 = new cell(); cell cell2 = new cell(); cell1 = stack.peeknew(i); cell2 = stack.peeknew(i+1); int k = ((cell1.getX()+1)*LENGTH)-5; int j = ((cell1.getY()+1)*LENGTH)-5; int m = ((cell2.getX()+1)*LENGTH)-5; int n = ((cell2.getY()+1)*LENGTH)-5; paintMoveBall3(k,j,m,n); } }}
    3.1.2 Maze类主要用于创建迷宫。迷宫的每个单元格长度为10,且迷宫长度为40*40。类中提供各个数据的访问器和修改器。
    package io.github.xieyezi;import java.util.ArrayList;import java.util.Random;public class Maze{ private int width = 40;// 迷宫宽度 private int height = 40;// 迷宫高度 private Random rnd = new Random();//用于创建迷宫时随机访问下一个点 public Maze() { } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public Maze(int width, int height) { super(); this.width = width; this.height = height; } public ArrayList<mazePoint> getMaze() { ArrayList<mazePoint> maze = new ArrayList<mazePoint>(); for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { mazePoint point = new mazePoint(w, h); maze.add(point); } } return CreateMaze(maze); } //创建迷宫 private ArrayList<mazePoint> CreateMaze(ArrayList<mazePoint> maze) { int top = 0; int x = 0; int y = 0; ArrayList<mazePoint> team = new ArrayList<mazePoint>(); team.add(maze.get(x + y * width)); while (top >= 0) { int[] val = new int[] { -1, -1, -1, -1 }; int times = 0; boolean flag = false; mazePoint pt = (mazePoint) team.get(top); x = pt.getX(); y = pt.getY(); pt.visted = true;// 记录单元格已访问 ro1: while (times < 4) { int dir = rnd.nextInt(4); if (val[dir] == dir) continue; else val[dir] = dir; switch (dir) { case 0: // 左边 if ((x - 1) >= 0 && maze.get(x - 1 + y * width).visted == false) { if (x == 0 && y == 0 || x == width && y == height) { top++; flag = true; break; } maze.get(x + y * width).setLeft(); maze.get(x - 1 + y * width).setRight(); team.add(maze.get(x - 1 + y * width)); top++; flag = true; break ro1; } break; case 1: // 右边 if ((x + 1) < width && maze.get(x + 1 + y * width).visted == false) { maze.get(x + y * width).setRight(); maze.get(x + 1 + y * width).setLeft(); team.add(maze.get(x + 1 + y * width)); top++; flag = true; break ro1; } break; case 2: // 上边 if ((y - 1) >= 0 && maze.get(x + (y - 1) * width).visted == false) { maze.get(x + y * width).setUp(); maze.get(x + (y - 1) * width).setDown(); team.add(maze.get(x + (y - 1) * width)); top++; flag = true; break ro1; } break; case 3: // 下边 if ((y + 1) < height && maze.get(x + (y + 1) * width).visted == false) { maze.get(x + y * width).setDown(); maze.get(x + (y + 1) * width).setUp(); team.add(maze.get(x + (y + 1) * width)); top++; flag = true; break ro1; } break; } times++; } if (!flag) { team.remove(top); top -= 1; } } int t = 0; while (t<maze.size()) { maze.get(t).setDirection(); t++; } return maze; }}
    3.1.3 mazePoint类主要用于表示迷宫里面每一个点。每一个点都有上下左右四个属性,在迷宫中的坐标,还有每个点四面空白墙壁的数量。类中提供各个数据的访问器和修改器。
    package io.github.xieyezi;public class mazePoint{ // “0”表示有墙,“1”表示无墙 private int left = 0; private int right = 0; private int up = 0; private int down = 0; // 迷宫中的坐标 private int x; private int y; public boolean visted; // 周围空白墙壁的数量 private int direction = 0; public mazePoint(int x, int y) { this.x = x; this.y = y; } public int getLeft() { return left; } public void setLeft() { this.left = 1; } public int getRight() { return right; } public void setRight() { this.right = 1; } public int getUp() { return up; } public void setUp() { this.up = 1; } public int getDown() { return down; } public void setDown() { this.down = 1; } public void setLeft(int left) { this.left = left; } public void setRight(int right) { this.right = right; } public void setUp(int up) { this.up = up; } public void setDown(int down) { this.down = down; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getDirection() { return direction; } public void setDirection() { if (this.up == 1) this.direction++; if (this.right == 1) this.direction++; if (this.down == 1) this.direction++; if (this.left == 1) this.direction++; }}
    3.1.4 cell类主要用于表示迷宫里面每一个单元格。每个单元格都有上下左右四个属性,在迷宫中的行列坐标,访问标记,以及每个单元格四面空白墙壁的数量。类中提供各个数据的访问器和修改器。
    package io.github.xieyezi;public class cell implements Comparable<cell>{ // “0”表示有墙,“1”表示无墙 private int left = 1; private int right = 1; private int up = 1; private int down = 1; private int dir;// 小球前进的方向,0,1,2,3分别代表上右下左 private int destion;// 存储单元格的位置 // 迷宫中的行列坐标 private int x; private int y; public boolean visted = false; //单元格的访问标记 private boolean pass = true;//是否为死路 private int direction = 0; //周围空白墙壁的数量 public cell() { x = 0; y = 0; } public cell(int x, int y) { this.x = x; this.y = y; this.pass=true; } public cell(int up, int right, int down, int left) { this.up = up; this.right = right; this.down = down; this.left = left; this.pass=true; setDirection(); } public int getLeft() { return left; } public void setLeft(int left) { this.left = left; } public int getRight() { return right; } public void setRight(int right) { this.right = right; } public int getUp() { return up; } public void setUp(int up) { this.up = up; } public int getDown() { return down; } public void setDown(int down) { this.down = down; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public boolean isVisted() { return visted; } public void setVisted(boolean visted) { this.visted = visted; } public int getDir() { return dir; } public void setDir(int dir) { this.dir = dir; } public int compareTo(cell o) { return this.destion - o.getDix(); } public int getDix() { return destion; } public void setDix(int dix) { this.destion = dix; } public boolean isPass() { return pass; } public void setPass() { this.pass = false; } public int getDirection() { return direction; } public void setDirection() { if (this.up == 1) this.direction++; if (this.right == 1) this.direction++; if (this.down == 1) this.direction++; if (this.left == 1) this.direction++; }}
    3.1.5 myStack类主要用于存储单元格的行列坐标。利用一个cell类的数组来存储单元格的坐标,栈的默认长度为1600。类中提供各个数据的访问器和修改器。
    package io.github.xieyezi;public class myStack{ private cell[] elements; private int size;//栈的长度 public static final int Default_capacity = 1600; //默认长度为1600 public myStack() { elements = new cell[Default_capacity]; } //把一个点压入栈中 public void push(cell value) { elements[size++] = value; } //删除栈顶的点并返回它 public cell pop() { return elements[--size]; } //返回栈顶的点 public cell peek() { return elements[size - 1]; } //返回栈中指定的点 public cell peeknew(int i) { return elements[i]; } //判断栈是否为空 public boolean empty() { return size == 0; } //返回栈的长度 public int getSize() { return size; }}
    3.2 绘制迷宫的实现利用Line类创建,先画出坐标轴的线。然后再根据每个点四个面的信息画线。
    3.3 动画效果的实现 创建一个TimeLine类的对象,利用模拟的小球圆心坐标的移动画线,从而展示出动态的效果。
    3.4 清空迷宫的实现利用面板的clear()方法清除装迷宫面板即可。
    3.5 退出游戏的实现利用exit()退出游戏即可。
    2 评论 72 下载 2018-10-29 10:56:10 下载需要12点积分
  • 基于JAVA的局域网聊天软件的设计与实现(仿制QQ)

    一、系统分析1.1 问题描述
    客户端

    实现简易版的局域网聊天器实现富文本内容聊天智能聊天机器人群发消息传送文件等功能
    服务器端

    实现群发通知管理聊天线程

    1.2 系统功能分析
    客户端功能

    登陆注册发送表情消息发送文本消息截取图片图片处理震动效果发送文件群发消息设置聊天文本样式
    服务器端

    广播通知

    1.3 开发平台及工具介绍
    Eclipse Mars2
    二、系统设计2.1 系统总体结构设计系统采用自己设计的网络消息传输协议,系统采用CS架构模式实现数据传送。


    2.2 系统各个类及类之间关系设计


    注:

    界面一般是继承了JFrame或JWindowJList、JTable所用的model和渲染器renderer都是自己重写的其余的一些比较琐碎的关系理起来比较复杂程序中的控件大多是自己自定义改写的
    2.3 数据存储的设计(文件等)
    采用键值对的方式存储账号密码
    截图默认保存在ScreenCut/+文件名.jpg
    用到了多种流操作,以及网络传输最关键的套接字操作

    2.4 界面设计
    自定义渲染和数据模型,实现JList、JTable的外交改变
    最长用的是在渲染器中继承JTable实现Renderer接口


    三、系统实现


    四、系统测试模块测试与系统测试:
    4.4 登陆测试
    4.5 注册测试
    4.6 表情测试
    4.7 震动测试震动效果无法截图!
    4.8 截图测试

    4.9 滤镜效果测试


    4.10 设置测试
    4.11 机器人聊天测试

    4.12 文件传输测试



    4.13 服务器管理界面测试

    五、总结通过这次的课程设计,可以说是开阔了自己的眼界,可以总结为以下几个方面:

    对JAVA体系的更深入的了解、对JAVA图像处理初步认识、对JAVA网络通信流的操作、流的套接更进一步深刻了解
    对网络通信的理解,CS、BS架构模式的了解
    对设计模式的初步认识与使用,线程管理这块单例模式的功能
    对JAVA的MVC模式的更深刻了解,层与层的分工明确,效率高,易管理,让人惊叹
    对线程的理解更深刻了,深谙UI主线程与处理耗时操作的子线程的逻辑处理关系
    对网络协议的初步了解,关于协议的定义了解,以及各种传送协议的效率比较有了大体。印象,Socket通信UDP和TCP通信机制初步了解
    对系统的架构设计有了初步认识,缓存机制,分布式系统等,虽然代码中还未实现,程序也并不是面向抽象编程和面向接口编程,代码低耦高聚效果一般,但仍然受益

    知识方面,在课程设计的任务中主要负责jlist jtable覆盖重写、基础类的继承重写、接口实现、界面设计与美化等。玩了半个假期后很多学过的知识都记不清楚了,所以打码的过程中经常翻书,让我又重温了知识;一部分知识书上没有,还需要上网查询或者找同学帮忙解决。有很多瓶颈时候,但坚持过去,看到一起完成的作品会有很大的满足感;其他方面:经过这次课程设计任务,我又一次认识到了团队合作的力量和重要性。一起讨论问题:苦恼过,失落过,兴奋过,到最后的成就感,让我成长,也对自己有了很大的信心。
    在这次课程设计,我们一起讨论要实现这个系统的哪些功能,把各自的想法说出来研究,我们还根据各自所学之长来分配工作,让我意识到在团队合作里每个人都能分享自己的想法、找到自己的位置发挥所长很重要,这样才能让我们更好地完成我们的工作。在完成我们的任务的过程中,我把每个功能逐步实现,比如在实现登录功能的时候,我会先把输入正确的账号和密码登录成功的功能实现,然后再实现判断输入的账号和密码是否一致,若不一致就返回重新输入账号密码这一功能,这让我在发现错误的时候更加容易找出并解决。在此过程虽然遇到许多困难,但是我都会去研究课本和课件里的例题或者上网去看教学视频,一步一步测试,自己实在无法解决了就去找同学帮助。
    经过这次课程设计,我对Java有了更深的了解,但这还远远不够,为了未来的发展,我必须更加努力地去学习更广更深的知识。
    7 评论 177 下载 2018-11-06 09:45:15 下载需要14点积分
  • 基于Android Studio实现的2048游戏

    1 需求分析1.1 背景与意义1.1.1 手机应用市场发展现状随着4G越来越普及以及手机应用的日益丰富还有智能水平的不断提高,从便携性和随身性这两方面来考虑,电脑所带来的体验已经不能跟手机相提并论了,它已经完美的超越了电脑。
    现如今Android、苹果等各智能手机已经基本占领整个手机市场,从而使得更多应用的出现,而手机游戏应用在其中占领主要位置。
    随着Android智能手机的普及以及游戏种类的多元化,使得Android手机游戏用户规模保持着稳步增长之势。
    1.1.2 国内外现状目前国内外的Android开发还是主要以应用开发为主,主要分成三类:企业应用、通用应用及游戏应用。企业应用的开发主要是一些大公司为了自己的品牌而开发的;通用应用主要是一些创业型公司或者独立开发者为了自己盈利开发的应用;游戏应用目前和通用应用相同。
    2048小游戏是一款最近风靡全球的手机游戏,简单的游戏模式和趣味的玩法,几乎游戏下载排行榜的前20名都可以看到“它的身影”。
    1.1.3 此游戏的意义现如今,手机游戏已在我们的生活中占据一席之地,并在一步步的壮大。可以说,随着它的迅猛发展,现今的手机游戏已经不单单是一种缓解压力的工具,而是形成了一种文化现象。随着游戏软件在市场的一步步壮大,与其有关的文化也随之传播。
    2048游戏的制作属于电子游戏中益智类小游戏,它做到了娱乐性、趣味性、教育性相统一。益智类的游戏即是需要去开动大脑思考从而获得游戏的胜利。简单的益智类游戏可以使玩家在娱乐中不断地开发大脑。这样一来就实现了在娱乐中学习。
    1.2 系统需求分析1.2.1 系统功能需求分析系统主要实现以下的几个功能:呈现游戏界面、重新开始游戏、当前分数和最高分数、游戏帮助等功能。
    重新开始游戏是当玩家无法满足当前进度时点击此按钮就会重新开始游戏,如果玩家处于不同关卡时提示重新开始游戏还是停留在此关卡。游戏帮助是当新手玩此游戏时无法知道游戏玩法时给予相应的提示。呈现游戏界面是游戏开始时主界面在游戏区域会生成4x4的矩阵同时在矩阵里面随机生成两个2或4的游戏卡片。当前分数和最高分数是显示此局玩家所获得的分数和历史上最高的分数,如果当前的分数超过最高的分数,那么最高分数显示当前的分数。如下图所示:

    1.2.2 游戏的基本规则在开始游戏后玩家通过滑动屏幕来操控卡片的移动方向,当卡片滑动中如果有两张卡片相同且他们中间也没有其他卡片时,在滑动的过程中这两张卡片会合并,显示为这两张卡片之和。在滑动过程中有三张卡片相同时只会合并向滑动方向两张卡片。在滑动中如果有两张卡片一样同时又有一张卡片的值跟这两张卡片相加的值时,滑动只会使那两张相同的卡片合并而不会接着让合并后的卡片和另一张卡片合并。
    2 系统分析与设计2.1 系统流程设计游戏进入开始页面,能够进入游戏的主界面并开始普通开局,从主界面能够重新开始游戏、查看帮助和进入关卡选择界面。当玩家点击重新开始按钮会弹出相应的对话框让玩家选择,如果玩家选择“是”时则重新开始游戏,如果选择“否”则返回游戏界面不做任何处理。在开始界面按返回按钮时则会退出游戏。
    游戏流程如下图所示:

    2.2 系统模块设计从总体出发,将该系统划分为三大模块:“菜单设计”、“界面设计”和“算法设计”。
    2.2.1 菜单设计菜单的实现是在游戏界面,可进一步划分为三个模块,分别是:“重新开始”、“退出游戏”、“游戏帮助”,如图所示:

    2.2.2 界面设计


    开始界面
    游戏界面









    2.2.3 算法设计当有两张卡片相同时,向它们可以碰撞的方向滑屏,卡片会移动到最底边并生成其两倍数字的卡片,并且生成一个“2”或者“4”的卡片。如图所示:



    生成2
    生成4









    当有两张卡片相同时,且在它们相同的方向有张跟他们之和的卡片,向它们可以碰撞的方向滑屏,相同的卡片会移动到无法移动的位置并生成其两倍数字的卡片,但合成的方向不会跟那两张数字的卡片合并,并且生成一个“2”或者“4”的卡片。如图所示:



    生成2
    生成4









    当界面上没有空位并且两两相邻的卡片不相同时游戏结束。如图所示:

    2.3 本章小结本章主要对游戏所实现的功能进行需求分析,分析了图形的特点和实现的可行性。对系统的性能进行了详细的分析。对系统的流程,系统所需的图形文件,系统的总体架构和系统用例进行了设计。通过本章的分析、设计能更加具体的了解系统功能,对系统所要实现的功能和图形文件有了更深的认识。为下一章系统功能的具体实现提供了可靠的参考依据。
    3 系统实现3.1 开始界面的实现游戏的主界面是按钮,只是为了实现界面的跳转,当玩家点击开始游戏就会调用loginActivity.java,此函数让页面跳转到游戏界面开始游戏,代码及图片如下所示:
    public class loginActivity extends MainActivity { protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.login); }}

    3.2 游戏界面的实现游戏界面主要是在activity_main.xml中当前分数、最高分数、重新开始按钮、退出游戏按钮、游戏帮助按钮、帮助按钮,当跳转到游戏界面时就会调用并执行MainActivity.java函数来展示游戏界面,代码及图片如下所示:
    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);}

    3.3 游戏滑动卡片移动的实现当玩家滑动屏幕时,主要通过GameView函数来监听玩家手指滑动的位置,先通过获取开始坐标和结束坐标,然后通过比较结束坐标跟开始坐标的差值来判断玩家是怎么滑动屏幕的。判断出玩家的滑动轨迹后,通过调用swipeLeft、swipeRight、swipeUp、swipeDown方法来实现卡片的移动,代码及图片如下所示:
    private void initGameView(){ setColumnCount(4); //将面板设置成4列 setBackgroundColor(0xffbbada0); System.out.println("initGameView"); setOnTouchListener(new View.OnTouchListener() { /* * startX:手机刚开始在屏幕上的X坐标 * startY:手机刚开始在屏幕上的Y坐标 * offsetX,offsetY,分别是手指在屏幕上的X,Y上的偏移量 */ private float startX,startY,offsetX,offsetY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: startX = event.getX(); startY = event.getY(); break; case MotionEvent.ACTION_UP: offsetX = event.getX() - startX; offsetY = event.getY() - startY; if(Math.abs(offsetX) > Math.abs(offsetY)){ if(offsetX < -5){ swipeLeft(); System.out.println("Left"); }else if(offsetX > 5){ swipeRight(); System.out.println("Right"); } } else{ if(offsetY < -5){ swipeUp(); System.out.println("Up"); }else if(offsetY > 5){ swipeDown(); System.out.println("Down"); } } break; } return true; } });}//向左滑动public void swipeLeft(){ boolean meger = false; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { for (int x1 = x+1; x1 < 4; x1++) { if(cardsMap[x1][y].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ /* * 将下标为(x,y)所在位置的卡片上的数字 * 设置为,坐标为(x1,y)所在位置的卡片上的值; * 第二步,将坐标(x1,y)所在位置的卡片上的数字 * 设置为0 * (即:变成空卡片) */ cardsMap[x][y].setNum( cardsMap[x1][y].getNum()); cardsMap[x1][y].setNum(0); x--; meger =true; break; }else if(cardsMap[x][y].equals(cardsMap[x1][y])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x1][y].setNum(0); MainActivity.getMainActivity(). addScore(cardsMap[x][y].getNum()); meger = true; } break; } } } } if(meger){ addRandomNum(); checkComplete(); }}//向右滑动public void swipeRight(){ boolean meger = false; for (int y = 0; y < 4; y++) { for (int x = 3; x >=0; x--) { for (int x1 = x-1; x1 >= 0; x1--) { if(cardsMap[x1][y].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ /* * 将下标为(x,y)所在位置的卡片上的数字 * 设置为,坐标为(x1,y)所在位置的卡片上的值; * 第二步,将坐标(x1,y)所在位置的卡片上的数字 * 设置为0 * (即:变成空卡片) */ cardsMap[x][y].setNum( cardsMap[x1][y].getNum()); cardsMap[x1][y].setNum(0); x++; meger =true; break; }else if(cardsMap[x][y].equals(cardsMap[x1][y])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x1][y].setNum(0); MainActivity.getMainActivity(). addScore(cardsMap[x][y].getNum()); meger =true; }break; } } } } if(meger){ addRandomNum(); checkComplete(); }}//向上滑动public void swipeUp(){ boolean meger = false; for (int x= 0; x< 4; x++) { for (int y = 0; y < 4; y++) { for (int y1 = y+1; y1 < 4; y1++) { if(cardsMap[x][y1].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ /* * 将下标为(x,y)所在位置的卡片上的数字 * 设置为,坐标为(x1,y)所在位置的卡片上的值; * 第二步,将坐标(x1,y)所在位置的卡片上的数字 * 设置为0 * (即:变成空卡片) */ cardsMap[x][y].setNum( cardsMap[x][y1].getNum()); cardsMap[x][y1].setNum(0); y--; meger =true; break; }else if(cardsMap[x][y].equals(cardsMap[x][y1])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x][y1].setNum(0); MainActivity.getMainActivity(). addScore(cardsMap[x][y].getNum()); meger =true; } break; } } } } if(meger){ addRandomNum(); checkComplete(); }}//向下滑动public void swipeDown(){ boolean meger = false; for (int x = 0; x< 4; x++) { for (int y = 3; y>= 0;y--) { for (int y1 = y-1; y1 >=0; y1--) { if(cardsMap[x][y1].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ /* * 将下标为(x,y)所在位置的卡片上的数字 * 设置为,坐标为(x1,y)所在位置的卡片上的值; * 第二步,将坐标(x1,y)所在位置的卡片上的数字 * 设置为0 * (即:变成空卡片) */ cardsMap[x][y].setNum( cardsMap[x][y1].getNum()); cardsMap[x][y1].setNum(0); y++; meger =true; break; }else if(cardsMap[x][y].equals(cardsMap[x][y1])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x][y1].setNum(0); MainActivity.getMainActivity(). addScore(cardsMap[x][y].getNum()); meger =true; } break; } } } } if(meger){ addRandomNum(); checkComplete(); }}



    效果1
    效果2









    3.4 重新开始游戏功能的实现当玩家点击游戏界面的重新开始游戏时,会弹出给玩家选择的对话框,让玩家选择“是”时游戏会重新开始,代码及图片如下所示:
    public void onClick(View view){ AlertDialog.Builder dialog3 = new AlertDialog.Builder(this); dialog3.setTitle("提示:"); dialog3.setMessage("你确定重新开始吗?"); dialog3.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); dialog3.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); dialog3.show(); }

    3.5 退出游戏功能的实现当玩家中途有事想退出游戏时会弹出给玩家选择的对话框,让玩家选择“是”时游戏会退出,代码及图片如下所示:
    public void onClick(View view){ AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setTitle("提示:"); dialog.setMessage("你确定要离开吗?"); dialog.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { System.exit(0); } }); dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); dialog.show(); }

    3.6 游戏帮助功能的实现当新玩家进入到游戏且不知道此游戏玩法时,玩家可以点击游戏帮助按钮来了解游戏玩法,点击按钮时游戏会弹出对话框显示游戏玩法,代码及图片如下所示:
    public void onClick(View view){ AlertDialog.Builder dialog2 = new AlertDialog.Builder(this); dialog2.setTitle("hey,guy!"); dialog2.setMessage("这么简单的游戏你确定需要帮助?"); dialog2.setNegativeButton("继续玩~", new DialogInterface.OnClickListener({ @Override public void onClick(DialogInterface dialog, int which) { } }); dialog2.show();}

    3.7 本章小结本章主要阐述本游戏相关功能的实现,详细的讲述了主界面的实现和各按钮功能的实现。
    4 测试本章主要对系统的功能进行测试,此次测试只是进行简单的调试,来确定游戏的各项功能是否能够正常运行。
    4.1 游戏流程测试该测试主要验证游戏能否实现场景的切换,当界面在开始界面时只显示按钮画面,当玩家点击此界面的开始按钮时跳转到游戏界面,如图所示:



    效果1
    效果2









    4.2 游戏模式该测试主要是测试游戏能否正常运行,当玩家滑动屏幕时能否正常的移动和当卡片相同时是否能够相加,还有就是测试游戏是否能正常结束。



    效果1
    效果2









    4.3 本章小结本章是对游戏系统进行简单的测试,通过测试可以看出此游戏可以正常的工作,同时一些功能也能够实现。
    5 总结本次课程设计的内容大部分都是参照课堂所讲以及一些网站给出的各种建议写的,在写的过程中遇到了很多问题,学到了很多东西。期间大概是因为基础不够好,只是找错误就花了很长时间。不过正因为这些错误,才能学到更多的知识,才能把知识点掌握的更牢靠,对于一些没有实现的功能,之后我一定会多花费些时间研究出来。我的课程设计优化的空间还相当大,希望老师能给出指导!
    11 评论 609 下载 2018-12-20 18:50:37 下载需要14点积分
显示 45 到 60 ,共 15 条
eject