From f792c9bc19935b3579b6b1fce1b01bd68b1c54f8 Mon Sep 17 00:00:00 2001 From: kiritow <1362050620@qq.com> Date: Fri, 8 Dec 2017 22:37:18 +0800 Subject: [PATCH] Big Update: 40% complete --- DBHomework.cbp | 78 +------ DBHomework.depend | 180 +++++++++++++--- DBHomework.layout | 79 ++++--- bs_util.h | 2 +- dbconfig.txt | 5 + design.txt | 354 +++++++++++++++++-------------- setup.sql | 40 +++- sql_log.txt | 5 + src/doAddBook.cpp | 121 +++++++++++ src/doAddBookObj.cpp | 141 ++++++++++++ src/doBorrowBook.cpp | 167 +++++++++++++++ src/doCheckLogin.cpp | 47 ++++ src/doCheckPermission.cpp | 79 +++++++ src/doGetBookByObj.cpp | 70 ++++++ src/doGetBookStatus.cpp | 81 +++++++ src/doGetBookTypes.cpp | 63 ++++++ src/doListBook.cpp | 88 ++++++++ src/{login.cpp => doLogin.cpp} | 6 +- src/doLogout.cpp | 48 +++++ src/doRemoveBook.cpp | 124 +++++++++++ src/doSearch.cpp | 73 +++++++ src/doViewBook.cpp | 80 +++++++ src/search.cpp | 147 ------------- web/booksys/addbook.html | 119 +++++++++++ web/booksys/addbookobj.html | 104 +++++++++ web/booksys/borrow-success.html | 27 +++ web/booksys/borrowbook.html | 71 +++++++ web/booksys/img/default_user.png | Bin 33820 -> 2168 bytes web/booksys/img/search_icon.png | Bin 0 -> 2204 bytes web/booksys/include/nav.css | 7 + web/booksys/install-success.html | 7 +- web/booksys/install.html | 7 +- web/booksys/listbook.html | 69 ++++++ web/booksys/login.html | 15 +- web/booksys/logout.html | 25 +++ web/booksys/mainpage.html | 30 ++- web/booksys/removebook.html | 102 +++++++++ web/booksys/replace_rule.txt | 30 +++ web/booksys/search.html | 57 +++++ web/booksys/util.js | 81 +++++++ web/booksys/viewbook.html | 130 ++++++++++++ 41 files changed, 2469 insertions(+), 490 deletions(-) create mode 100644 dbconfig.txt create mode 100644 sql_log.txt create mode 100644 src/doAddBook.cpp create mode 100644 src/doAddBookObj.cpp create mode 100644 src/doBorrowBook.cpp create mode 100644 src/doCheckLogin.cpp create mode 100644 src/doCheckPermission.cpp create mode 100644 src/doGetBookByObj.cpp create mode 100644 src/doGetBookStatus.cpp create mode 100644 src/doGetBookTypes.cpp create mode 100644 src/doListBook.cpp rename src/{login.cpp => doLogin.cpp} (94%) create mode 100644 src/doLogout.cpp create mode 100644 src/doRemoveBook.cpp create mode 100644 src/doSearch.cpp create mode 100644 src/doViewBook.cpp delete mode 100644 src/search.cpp create mode 100644 web/booksys/addbook.html create mode 100644 web/booksys/addbookobj.html create mode 100644 web/booksys/borrow-success.html create mode 100644 web/booksys/borrowbook.html create mode 100644 web/booksys/img/search_icon.png create mode 100644 web/booksys/include/nav.css create mode 100644 web/booksys/listbook.html create mode 100644 web/booksys/removebook.html create mode 100644 web/booksys/replace_rule.txt create mode 100644 web/booksys/search.html create mode 100644 web/booksys/util.js create mode 100644 web/booksys/viewbook.html diff --git a/DBHomework.cbp b/DBHomework.cbp index 575d2ed..53e9103 100644 --- a/DBHomework.cbp +++ b/DBHomework.cbp @@ -31,99 +31,37 @@ + + + - - + - - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DBHomework.depend b/DBHomework.depend index 59202df..28dd2eb 100644 --- a/DBHomework.depend +++ b/DBHomework.depend @@ -1,24 +1,16 @@ # depslib dependency file v1.0 -1510706148 source:d:\codeblocks_codes\dbhomework\main.cpp - - - - - - - - - "Request.h" - "Response.h" +1511504972 source:d:\codeblocks_codes\dbhomework\main.cpp + "Session.h" "Util.h" - "MySQLWrapper.h" + "jsonfail.h" "json.hpp" + "MySQLTransaction.h" 1511076615 d:\codeblocks_codes\dbhomework\mysqlwrapper\mysqlwrapper.h -1511076563 source:d:\codeblocks_codes\dbhomework\mysqlwrapper\mysqlwrapper.cpp +1511877230 source:d:\codeblocks_codes\dbhomework\mysqlwrapper\mysqlwrapper.cpp "MySQLWrapper.h" "MySQLInclude.h" @@ -96,38 +88,39 @@ "Util.h" -1510899241 source:d:\codeblocks_codes\dbhomework\httpwrapper\request.cpp +1511792433 source:d:\codeblocks_codes\dbhomework\httpwrapper\request.cpp "Request.h" "Util.h" + -1510899188 d:\codeblocks_codes\dbhomework\httpwrapper\request.h +1511686368 d:\codeblocks_codes\dbhomework\httpwrapper\request.h "Session.h" -1511136347 source:d:\codeblocks_codes\dbhomework\httpwrapper\response.cpp +1511686368 source:d:\codeblocks_codes\dbhomework\httpwrapper\response.cpp "Response.h" -1511136341 d:\codeblocks_codes\dbhomework\httpwrapper\response.h +1511686368 d:\codeblocks_codes\dbhomework\httpwrapper\response.h "CookieVec.h" -1511153903 source:d:\codeblocks_codes\dbhomework\httpwrapper\util.cpp +1511686368 source:d:\codeblocks_codes\dbhomework\httpwrapper\util.cpp "Util.h" -1511153929 d:\codeblocks_codes\dbhomework\httpwrapper\util.h +1511689007 d:\codeblocks_codes\dbhomework\httpwrapper\util.h - "MySQLWrapper.h" "Response.h" + "MySQLWrapper.h" 1510704940 d:\codeblocks_codes\dbhomework\json.hpp @@ -163,7 +156,7 @@ "Util.h" -1510973351 d:\codeblocks_codes\dbhomework\httpwrapper\cookievec.h +1511791095 d:\codeblocks_codes\dbhomework\httpwrapper\cookievec.h @@ -201,7 +194,7 @@ -1511147480 d:\codeblocks_codes\dbhomework\httpwrapper\session.h +1511250624 d:\codeblocks_codes\dbhomework\httpwrapper\session.h "Request.h" "Response.h" @@ -219,18 +212,20 @@ "Util.h" "json.hpp" -1511149331 source:d:\codeblocks_codes\dbhomework\login.cpp +1511687998 source:d:\codeblocks_codes\dbhomework\login.cpp + "Request.h" + "Response.h" "Session.h" "Util.h" - "json.hpp" - "jsonfail.h" + "MySQLWrapper.h" + "MySQLTransaction.h" 1511136779 source:d:\codeblocks_codes\dbhomework\addbooktype.cpp "Session.h" "Util.h" "json.hpp" -1511149002 source:d:\codeblocks_codes\dbhomework\jsonfail.cpp +1511158571 source:d:\codeblocks_codes\dbhomework\jsonfail.cpp "jsonfail.h" 1511153994 d:\codeblocks_codes\dbhomework\jsonfail.h @@ -259,3 +254,136 @@ 1511156696 d:\codeblocks_codes\dbhomework\mysqltransaction.h "MySQLWrapper.h" +1511248736 source:d:\codeblocks_codes\dbhomework\dopaste.cpp + "Request.h" + "Response.h" + "Util.h" + "json.hpp" + + + "windows.h" + +1511878138 source:d:\codeblocks_codes\dbhomework\httpwrapper\session_mysql.cpp + "Session.h" + "Singleton.hpp" + "Util.h" + "MySQLWrapper.h" + "MySQLTransaction.h" + + + + +1511337863 source:d:\codeblocks_codes\dbhomework\blockuser.cpp + "Session.h" + "Util.h" + "jsonfail.h" + "json.hpp" + +1511338065 source:d:\codeblocks_codes\dbhomework\allowuser.cpp + "Session.h" + "Util.h" + "jsonfail.h" + "json.hpp" + +1511416570 source:d:\codeblocks_codes\dbhomework\addbookobject.cpp + "Session.h" + "Util.h" + "jsonfail.h" + "json.hpp" + "MySQLTransaction.h" + +1511877177 d:\codeblocks_codes\dbhomework\mysqlwrapper\mysqltransaction.h + "MySQLWrapper.h" + +1511877161 source:d:\codeblocks_codes\dbhomework\mysqlwrapper\mysqltransaction.cpp + "MySQLTransaction.h" + +1511687385 source:d:\codeblocks_codes\dbhomework\install.cpp + "Request.h" + "Response.h" + "Session.h" + "Util.h" + "MySQLWrapper.h" + "MySQLTransaction.h" + +1511790374 source:d:\codeblocks_codes\dbhomework\old\login.cpp + "Session.h" + "Util.h" + "json.hpp" + "jsonfail.h" + +1510704940 d:\codeblocks_codes\dbhomework\\json.hpp + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1511153994 d:\codeblocks_codes\dbhomework\\jsonfail.h + +1511940029 source:d:\codeblocks_codes\dbhomework\src\login.cpp + "bs_util.h" + + + +1511939814 source:d:\codeblocks_codes\dbhomework\bs_util.cpp + "bs_util.h" + +1512014783 d:\codeblocks_codes\dbhomework\\bs_util.h + "Request.h" + "Response.h" + "Session.h" + "json.hpp" + "jsonfail.h" + "Util.h" + +1512014783 d:\codeblocks_codes\dbhomework\bs_util.h + "Request.h" + "Response.h" + "Session.h" + "json.hpp" + "jsonfail.h" + "Util.h" + +1511941430 source:d:\codeblocks_codes\dbhomework\src\dochecklogin.cpp + "bs_util.h" + +1511963515 source:d:\codeblocks_codes\dbhomework\src\dogetbookstatus.cpp + "bs_util.h" + +1511947446 source:d:\codeblocks_codes\dbhomework\src\doviewbook.cpp + "bs_util.h" + +1512096115 source:d:\codeblocks_codes\dbhomework\src\doborrowbook.cpp + "bs_util.h" + "MySQLTransaction.h" + +1512108415 source:d:\codeblocks_codes\dbhomework\src\doaddbook.cpp + "bs_util.h" + +1512703018 source:d:\codeblocks_codes\dbhomework\src\doaddbookobj.cpp + "bs_util.h" + "MySQLTransaction.h" + diff --git a/DBHomework.layout b/DBHomework.layout index 98df725..9f6eacc 100644 --- a/DBHomework.layout +++ b/DBHomework.layout @@ -1,100 +1,95 @@ - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - + diff --git a/bs_util.h b/bs_util.h index aa08a9d..b955303 100644 --- a/bs_util.h +++ b/bs_util.h @@ -19,4 +19,4 @@ int getPermissionLevel(const std::string& Username,Response& res); std::function SQLParseInt(int& nval); #define postval(NAME) if(req.post[#NAME].empty()){jsonfail(err_missing_parameter);break;}string NAME=req.post[#NAME] -#define startdb() DBInfo db;if(db.readConfig()<0){jsonfail(err_config);break;} MySQLConn conn;if(db.connectProxy(conn)<0){jsonfail(err_connect);break;} +#define startdb() DBInfo db;if(db.readConfig()<0){jsonfail(err_config);break;} MySQLConn conn;if(db.connectProxy(conn)<0){jsonfail(err_connect);break;}if(conn.exec("set names utf8",nullptr)<0){jsonfail(err_sql,"Failed to set names");break;} diff --git a/dbconfig.txt b/dbconfig.txt new file mode 100644 index 0000000..2b098f3 --- /dev/null +++ b/dbconfig.txt @@ -0,0 +1,5 @@ +127.0.0.1 +root +ilovelgj +abc +3306 diff --git a/design.txt b/design.txt index bdf0f29..3f30711 100644 --- a/design.txt +++ b/design.txt @@ -1,118 +1,146 @@ -ݿҵ: - +数据库作业: +语言 JavaScript,HTML,C++ -ݿ: +数据库: MySQL =================== -ݿ +数据库表设计 =================== -ǰ׺: bs_ -û(bs_user) - ¼˺ varchar - ¼ varchar (hashed) - dz varchar - û integer (0 Ա 1 Ա 2 ͼݽĴ 3 ) - 3ûжӦûϢ! - ˻״̬ integer (0 1 ֹ½ 2 Ҫ֤ܼ 3 ½) - ״̬: Ըõ½˺ŵ½ʱʾ˻,עʱʾܲע - ֹ½: Ըõ½˺ŵ½ʱʾ˻ѱ - ֤: עûҪ֤½ʱʾ֤. ֤״̬Ϊ3 - ½: һ +表前缀: bs_ +用户表(bs_user) + 登录账号 varchar 主键 + 登录密码 varchar (hashed) + 昵称 varchar + 用户级别 integer (0 超级管理员 1 管理员 2 图书馆借阅处 3 读者) + 3级用户必须有对应的用户信息! + 账户状态 integer (0 隐藏 1 禁止登陆 2 需要验证才能激活 3 允许登陆) + 隐藏状态: 以该登陆账号登陆时会提示账户不存在,但注册时会提示不能不注册 + 禁止登陆: 以该登陆账号登陆时会提示账户已被禁用 + 待验证: 刚注册完的用户需要经过验证,登陆时会提示待验证. 验证后状态变为3 + 允许登陆: 一切正常 -(bs_level) - ȼ integer - Ĭͽ޶ integer - Ĭ߽޶ integer - Ĭͽʱ integer - Ĭ߽ʱ integer - ɽܵС money +级别表(bs_level) + 等级 integer 主键 + 默认最低借阅限额 integer + 默认最高借阅限额 integer + 默认最低借阅时间 integer + 默认最高借阅时间 integer + 可接受的最小余额额度 money -߱(bs_reader) - ¼˺ bs_user - varchar - ֤ varchar - ϵ绰 varchar - ȼ integer (->->ı޶Ϣ) bs_level - ɳֵ(ֵ) integer (ǩ;ֵȵ) - ޶ integer (뼶еĬֵһ,ʱܵӰ) - ʹýĶ integer - ʱ޶ integer () - integer (ǩ,,) - money +读者表(bs_reader) + 登录账号 外键到bs_user + 姓名 varchar + 身份证号 varchar + 联系电话 varchar + 等级 integer (触发器->升级->改变限额等信息) 外键到bs_level + 成长值(经验值) integer (签到送经验值等等) + 借阅限额 integer (可以与级别表中的默认值不一致,但升级时会受到影响进而重新评估) + 已使用借阅额度 integer 冗余 + 借阅时间限额 integer (天) + 积分 integer (签到,奖励,活动) + 余额 money -ͼ(bs_booktype) - varchar +图书类别表(bs_booktype) + 类别名称 varchar 主键 -ͼ(bs_book) - ͼ鶨 integer (ǵЩûISBN) - ISBN varchar - varchar - bs_booktype - varchar - varchar - date - ״̬ integer (0 1 2 ) - : ͼʾⱾϢ - : ֹⱾ +图书表(bs_book) + 图书定义序号 integer 主键 (考虑到有些书没有ISBN号) + ISBN号 varchar 主键 + 名称 varchar + 类别 外键到bs_booktype + 作者 varchar + 出版社 varchar + 出版日期 date + 状态 integer (0 隐藏 1 禁用 2 正常) + 隐藏: 不在图书搜索中显示这本书的信息 + 禁用: 禁止借阅这本书 -ͼ״̬(bs_bookstatus) - ͼʵ integer (һͼж౾) - ͼ bs_book - λ varchar (ʾͼλ) - ״̬ integer (0 1 ʧ 2 ڹ) - : 鼮ѽ,bs_borrowвҵ - ʧ: 鼮Ѷʧ,޷вҵйϢ.(ڱ涪ʧ) - ڹ: 鼮ڹ,Խ +图书状态表(bs_bookstatus) + 图书实体序号 integer 主键 (一本书可能在图书馆里有多本藏书) + 图书类别序号 外键到bs_book + 位置 varchar (用来提示图书所在位置) + 状态 integer (0 借出 1 丢失 2 在馆) + 借出: 书籍已借出,可以在bs_borrow中查找到 + 丢失: 书籍已丢失,且无法从其他表中查找到有关信息.(用于报告丢失) + 在馆: 书籍在馆,可以借出 -Ĺϵ(bs_borrow) - ˺ bs_user - ͼʵ bs_bookstatus - date - ٻ date (ʹûڽͼ,ҲĶ. ڽ鼮ʱ) - date Ϊ ûnullʾѹ黹Ϊʷݣ +借阅关系表(bs_borrow) + 借阅者账号 外键到bs_user + 图书实体序号 外键到bs_bookstatus + 借阅日期 date + 最迟还书日期 date (即使用户在借阅图书后升级,这个日期也不会改动. 此日期是在借阅书籍的时候计算的) + 还书日期 date 可以为空 (如果没还书就是null,否则表示书已归还,此条数据留存为历史数据) =================== - +特性设计 =================== -5Ӱװ(߰װ,Wordpress Install) -Ա˺ ޸˻룬˻ֻһ,ӹԱ˺,ͬԱ˺ -Ա˺ ޸Լͨû,/޸ͼ,ͬͼݽĴ˺ -ͼݽĴ˺ ޸Լ,/޸ĽĹϵ +5分钟安装(在线安装,类似Wordpress Install) +超级管理员账号 可以修改所有账户的密码,此账户只能有一个,可以添加管理员账号,其他功能同管理员账号 +管理员账号 可以修改自己和所有普通用户的密码,添加/修改图书类别表,其他功能同图书馆借阅处账号 +图书馆借阅处账号 可以修改自己的密码,添加/修改借阅关系表 =================== - +具体设计 =================== -5ӰװҪϢ: - ݿַ dbaddr - ݿ˿ dbport - ݿ˻ dbuser - ݿ dbpass - ݿ dbname - Ա supass (Ա˻Ϊroot) +5分钟安装需要的信息: + 数据库服务器地址 dbaddr + 数据库端口 dbport + 数据库账户 dbuser + 数据库口令 dbpass + 数据库名称 dbname + 超级管理员口令 supass (超级管理员账户锁定为root) -½˺: +新建账号: session + +http://localhost/cgi-bin/booksys/admin/install + 安装页面. 安装成功后再访问本页面将重定向到login. + +http://localhost/cgi-bin/booksys/login + 登录页面 + +http://localhost/cgi-bin/booksys/mainpage + 主页(搜索框) + +http://localhost/cgi-bin/booksys/dashboard + 个人页面(borrowed book那些) -ɾIJ ͼͼ飬˻ +http://localhost/cgi-bin/booksys/info + +http://localhost/cgi-bin/booksys/admin/workbench + +http://localhost/cgi-bin/booksys/admin/booktype + +http://localhost/cgi-bin/booksys/admin/newbook + +http://localhost/cgi-bin/booksys/viewbook? + +http://localhost/cgi-bin/booksys/ + + + + + +增删改查 : 图书类别,图书,账户 http://booksys.com/api/v1/install POST (install.cpp) POST: dbaddr,dbport,dbuser,dbpass,dbname,supass http://booksys.com/api/v1/search?type=...&name=... GET (search.cpp) GET: - type= 0 û 1 鼮 - name= û,鼮 - typename= 鼮,typeΪ1ʱЧ + type= 0 用户 1 书籍 + name= 用户名,书籍名称 + typename= 书籍种类名称,仅当type为1时有效 http://booksys.com/api/v1/explore GET GET: - ûв + 没有参数 http://booksys.com/api/v1/addbook POST (addbook.cpp) POST: - (ͼ鶨Զ) + (图书定义序号自动生成) isbn bookname booktype @@ -121,14 +149,14 @@ pubdate status - : - Ȩ޲,ʧ + 错误: + 权限不足,添加失败 http://booksys.com/api/v1/editbook POST (editbook.cpp) POST: - book_key (Ψһͼ鶨) + book_key (唯一图书定义序号) - оΪѡ: һдʱݵ޸ + 以下所有列均为可选: 当任一列存在时将引起数据的修改 isbn bookname booktype @@ -137,40 +165,40 @@ pubdate status - : - Ȩ޲,޸ʧ + 错误: + 权限不足,修改失败 http://booksys.com/api/v1/removebook POST (removebook.cpp) POST: - book_key (Ψһͼ鶨) + book_key (唯一图书定义序号) - : - ͼһʵ崦ڽ״̬ʱɾͼ鶨. + 错误: + 当图书的任一实体处于借出状态时将不能删除图书定义. http://booksys.com/api/v1/addbooktype POST (addbooktype.cpp) POST: booktype - : - µƷͻ + 错误: + 当新的类型名称发生冲突 http://booksys.com/api/v1/editbooktype POST (editbooktype.cpp) POST: booktype_old booktype_new - : - µƷͻ + 错误: + 当新的类型名称发生冲突 - : - ıͼԭͼϢ(δʵ) + 副作用: + 改变图书种类会引起所有与原种类相关图书的信息变更(未实现) http://booksys.com/api/v1/removebooktype POST (removebooktype.cpp) POST: booktype - : - һͼ鶨ʹô˶ʱɾͼ + 错误: + 当任一图书定义使用此定义时将不能删除图书种类 http://booksys.com/api/v1/join POST (join.cpp) POST: @@ -178,10 +206,10 @@ pass nickname - ʾ:ͨAPIעûȼΪ3. ע˻״̬2 + 提示:通过此API注册的用户等级锁定为3. 且注册后账户处于状态2 - : - ˻ظʱ + 错误: + 账户名称重复时 http://booksys.com/api/v1/enableuser POST (enableuser.cpp) POST: @@ -190,122 +218,122 @@ realid realphone - ʾ:ͨAPIûȼΪ3.(ߵȼûҪ˼) ˻״̬ԶΪ3. + 提示:通过此API激活的用户等级必须为3.(更高等级的用户不需要经此激活) 激活后账户状态将自动变更为3. - : - ֹ. + 错误: + 内容填充错误或禁止激活. http://booksys.com/api/v1/login POST (login.cpp) POST: account pass - ʾ:ͨAPIɵ½ûȼΪ0,1,2,3,˻״̬Ϊ3ܵ½ɹ,˻״̬Ϊ2ʱʾϢǰ,˻״̬Ϊ1ʱؽֹ½,˻״̬Ϊ0ʱҪϵԱϢ. + 提示:通过此API可登陆的用户等级为0,1,2,3,账户状态为3才能登陆成功,账户状态为2时返回提示信息前往激活,账户状态为1时返回禁止登陆,账户状态为0时返回需要联系管理员激活信息. - : - ˻(˻) + 错误: + 账户或密码错误(包括账户错误) - : - ½ɹʱUserSession. + 返回: + 当登陆成功时绑定User到Session. http://booksys.com/api/v1/logout GET/POST (logout.cpp) - ʾ:˳½. עSession. + 提示:退出登陆. 会立刻注销Session. http://booksys.com/api/v1/addbookobject POST (addbookobject.cpp) POST: - class_id ͼ鶨 - book_id ͼʵ(ѡ,д˲ʱԴ˲½鼮) - position λ - status ͼ״̬ + class_id 图书定义序号 + book_id 图书实体序号(可选,当有此参数时将尝试以此参数新建书籍) + position 藏书位置 + status 图书状态 - : - Ȩ޲ - ͼʵųͻ + 错误: + 权限不足 + 图书实体序号冲突 http://booksys.com/api/v1/editbookobject POST POST: - book_id_old ͼʵ - book_id_new ͼʵ (ѡ) - position_new ²λ (ѡ) - status ͼ״̬ + book_id_old 旧图书实体序号 + book_id_new 新图书实体序号 (可选) + position_new 新藏书位置 (可选) + status 图书新状态 - : - Ȩ޲ - ͼʵ岻 - ͼʵųͻ + 错误: + 权限不足 + 图书实体不存在 + 图书实体序号冲突 http://booksys.com/api/v1/removebookobject POST POST: - book_id ͼʵ + book_id 图书实体序号 - : - Ȩ޲ - ͼʵŲ - ͼѽ,ɾʵ + 错误: + 权限不足 + 图书实体序号不存在 + 图书已借出,不能删除实体 http://booksys.com/api/v1/borrowbook POST POST: - account ˻ - book_id ͼʵ + account 借阅者账户 + book_id 图书实体序号 - : - Ȩ޲(APIֻͼԱ(ȼ<=2)) - ͼ鲻ɽ - ʵ岻 + 错误: + 权限不足(此API只能由图书管理员以上(等级<=2)调用) + 图书不可借阅 + 实体不存在 http://booksys.com/api/v1/returnbook POST POST: - book_id ͼʵ + book_id 图书实体序号 - : - ͼ״̬(δȵ) - ʵ岻 + 错误: + 图书状态不允许还书(未借出等等) + 实体不存在 http://booksys.com/api/v1/blockuser POST (blockuser.cpp) POST: - account û + account 被操作用户 - ʾ: - ޸û״̬Ϊ1(ֹ½) + 提示: + 本操作将修改用户的状态为1(禁止登陆) - : - Ȩ޲. ֻм<=1ûʹñAPI. йԱֻ޸Լ͵ȼ>=2û. Ա޸ȫ˻. + 错误: + 权限不足. 只有级别<=1的用户可以使用本API. 其中管理员只能修改自己和等级>=2的用户. 超级管理员可以修改全部账户. http://booksys.com/api/v1/allowuser POST (allowuser.cpp) POST: - account û + account 被操作用户 - ʾ: - ޸û״̬Ϊ3(). ע: ûδ֤,֤. + 提示: + 本操作将修改用户的状态为3(正常). 注意: 若用户尚未完成身份验证,将忽视身份验证步骤. - : - Ȩ޲. ֻм<=1ûʹñAPI.йԱֻ޸Լ͵ȼ>=2û. Ա޸ȫ˻. + 错误: + 权限不足. 只有级别<=1的用户可以使用本API.其中管理员只能修改自己和等级>=2的用户. 超级管理员可以修改全部账户. http://booksys.com/api/v1/dashboard GET GET: - ޲ + 无参数 - : - عڵǰûȫϢ. (Session) + 返回: + 返回关于当前用户的全部信息. (Session关联) - : - δ¼ + 错误: + 未登录 http://booksys.com/api/v1/adminsearch POST POST: - ʾ: - Աר + 提示: + 管理员专用搜索 - : - Ȩ޲. ֻйԱ<=1ʹñAPI + 错误: + 权限不足. 只有管理员<=1能使用本API http://booksys.com/api/v1/advancedsearch POST POST: - ʾ: + 提示: - : - Ȩ޲㣬ֻ<=2ûʹñAPI + 错误: + 权限不足,只有<=2的用户能使用本API \ No newline at end of file diff --git a/setup.sql b/setup.sql index 6ba5c5b..e27d0c6 100644 --- a/setup.sql +++ b/setup.sql @@ -93,4 +93,42 @@ create table bs_session id char(32) primary key, last_time integer, username varchar(10) references bs_user(username) -); \ No newline at end of file +); + +DROP PROCEDURE IF EXISTS newbook; +DELIMITER // +create procedure newbook( + IN isbn varchar(13), + IN name varchar(20), + IN book_type varchar(10), + IN author varchar(10), + IN publisher varchar(20), + IN publish_time date, + IN status integer +) +begin + declare maxid integer; + declare idcnt integer; + declare result_code integer default 0; + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET result_code = 1; + + start transaction; + select count(class_id) into idcnt from bs_book ; + select max(class_id) into maxid from bs_book; + + if (idcnt=0) then + set maxid=1; + else + set maxid=maxid+1; + end if; + insert into bs_book values (maxid,isbn,name,book_type,author,publisher,publish_time,status); + if result_code=1 then + rollback; + else + commit; + end if; + + select result_code; +end +// +DELIMITER ; diff --git a/sql_log.txt b/sql_log.txt new file mode 100644 index 0000000..c7ef9cc --- /dev/null +++ b/sql_log.txt @@ -0,0 +1,5 @@ +start transaction +select count(id) from bs_session where id='0284A830DCADD84758B2F1E945C744FA' +insert into bs_session values ('0284A830DCADD84758B2F1E945C744FA',1512108423,null) +commit +select username from bs_session where id='0284A830DCADD84758B2F1E945C744FA' diff --git a/src/doAddBook.cpp b/src/doAddBook.cpp new file mode 100644 index 0000000..f51813b --- /dev/null +++ b/src/doAddBook.cpp @@ -0,0 +1,121 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(book_isbn); + postval(book_type); + postval(book_name); + postval(book_author); + postval(book_publish); + postval(book_pubdate); + postval(book_status); + + int book_status_real=ParseInt(book_status); + if(book_status_real<0) + { + jsonfail(err_parameter,"Failed to parse status"); + break; + } + + if(book_status_real<0||book_status_real>2) + { + jsonfail(err_parameter,"Invalid Status"); + break; + } + + startdb(); + + /// Check Permission + int permission_level=-1; + if(conn.exec(make_str("select permission_level from bs_user where username='", + se.getUser(), + "'"), + SQLParseInt(permission_level))<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + if(permission_level<0) + { + jsonfail(err_data,"Failed to get permission level"); + break; + } + + if(permission_level>2) + { + jsonfail(err_permission_denied,"Permission Not Reach Required Level"); + break; + } + + int result_code=-1; + if(conn.exec(make_str("call newbook('", + book_isbn, + "','", + book_name, + "','", + book_type, + "','", + book_author, + "','", + book_publish, + "','", + book_pubdate, + "',", + book_status_real, + ")"), + SQLParseInt(result_code))<0) + { + jsonfail(err_sql,"Procedure 1"); + break; + } + + if(result_code==1) + { + /// Failed + jsonfail(err_sql_logic,"SQL Operation Failed"); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doAddBookObj.cpp b/src/doAddBookObj.cpp new file mode 100644 index 0000000..c00d509 --- /dev/null +++ b/src/doAddBookObj.cpp @@ -0,0 +1,141 @@ +#include "bs_util.h" +#include "MySQLTransaction.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(id); + postval(book_pos); + postval(bookobj_status); + + int id_real=ParseInt(id); + if(id_real<0) + { + jsonfail(err_data,"Failed to parse id."); + break; + } + + int bookobj_status_real=ParseInt(bookobj_status); + if(bookobj_status_real<0) + { + jsonfail(err_data,"Failed to parse obj status"); + break; + } + + startdb(); + + Transaction ts(conn); + + /// Check Permission + int permission_level=-1; + if(conn.exec(make_str("select permission_level from bs_user where username='", + se.getUser(), + "'"), + SQLParseInt(permission_level))<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + if(permission_level<0 || permission_level>=3) + { + jsonfail(err_permission_denied); + break; + } + + /// Check If this is the first book object + int count_val=-1; + if(conn.exec("select count(book_id) from bs_bookstatus", + SQLParseInt(count_val))<0) + { + jsonfail(err_sql,"Step 2"); + break; + } + + if(count_val<0) + { + jsonfail(err_general,"This error should not exist."); + break; + } + + int current_maxbook_id=-1; + if(count_val==0) + { + current_maxbook_id=0; + } + else + { + /// Get Available BookObjID + + if(conn.exec("select max(book_id) from bs_bookstatus", + SQLParseInt(current_maxbook_id))<0) + { + jsonfail(err_sql,"Step 3"); + break; + } + + if(current_maxbook_id<0) + { + jsonfail(err_data,"Failed to generate book id."); + break; + } + } + + int nextbook_id=current_maxbook_id+1; + + /// Insert the book.(TODO) + if(conn.exec(make_str("insert into bs_bookstatus values (", + nextbook_id, + ",", + id_real, + ",'", + book_pos, + "',", + bookobj_status_real, + ")"), + nullptr)<0) + { + jsonfail(err_sql,"Step 4"); + break; + } + + ts.commit(); + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} diff --git a/src/doBorrowBook.cpp b/src/doBorrowBook.cpp new file mode 100644 index 0000000..53806fb --- /dev/null +++ b/src/doBorrowBook.cpp @@ -0,0 +1,167 @@ +#include "bs_util.h" +#include "MySQLTransaction.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(bid); + + int bid_real=ParseInt(bid); + if(bid_real<0) + { + jsonfail(err_parameter,"Failed to parse bid"); + break; + } + + startdb(); + + /// Start transaction + Transaction ts(conn); + if(!ts.isReady()) + { + jsonfail(err_sql_logic,"Failed to start transaction."); + break; + } + + /// Check if user can borrow... + int cntval; + if(conn.exec(make_str("select count(username) from bs_reader where username='", + se.getUser(), + "'"), + SQLParseInt(cntval))<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + if(cntval!=1) + { + jsonfail(err_permission_denied,"You are not in reader list!"); + break; + } + + int borrow_limit,borrow_used,borrow_time_limit; + if(conn.exec(make_str("select borrow_limit,borrow_used,borrow_time_limit from bs_reader where username='", + se.getUser(), + "'"), + [&](MySQLResult& res) + { + res.stepRow([&](char** val,unsigned long* len) + { + borrow_limit=ParseInt(val[0]); + borrow_used=ParseInt(val[1]); + borrow_time_limit=ParseInt(val[2]); + }); + })<0) + { + jsonfail(err_sql,"Step 2"); + break; + } + + if(borrow_limit-borrow_used<=0) + { + jsonfail(err_general,"Reach Borrow Limit"); + break; + } + + /// Verify bid + if(conn.exec(make_str("select count(book_id) from bs_bookstatus where book_id=", + bid_real), + SQLParseInt(cntval))<0) + { + jsonfail(err_sql,"Step 3"); + break; + } + + if(cntval!=1) + { + jsonfail(err_parameter,"Failed to verify bid"); + break; + } + + /// Check if bid is allowed to be borrowed. + int book_status; + if(conn.exec(make_str("select status from bs_bookstatus where book_id=",bid_real), + SQLParseInt(book_status))<0) + { + jsonfail(err_sql,"Step 4"); + break; + } + + if(book_status!=2) + { + jsonfail(err_data,"Book is not allowed to borrow"); + break; + } + + /// DO UPDATE + if(conn.exec(make_str("update bs_reader set borrow_used=borrow_used+1 where username='", + se.getUser(), + "'"),nullptr)<0) + { + jsonfail(err_sql,"Update 1"); + break; + } + + if(conn.exec(make_str("update bs_bookstatus set status=0 where book_id=",bid_real),nullptr)<0) + { + jsonfail(err_sql,"Update 2"); + break; + } + + if(conn.exec(make_str("insert into bs_borrow values ('", + se.getUser(), + "',", + bid_real, + ",curdate(),date_add(curdate(),interval ", + borrow_time_limit, + " day),null)"),nullptr)<0) + { + jsonfail(err_sql,"Update 3"); + break; + } + + /// Commit it + ts.commit(); + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doCheckLogin.cpp b/src/doCheckLogin.cpp new file mode 100644 index 0000000..8327fa4 --- /dev/null +++ b/src/doCheckLogin.cpp @@ -0,0 +1,47 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + j["success"]=1; + j["next_url"]="/booksys/mainpage.html"; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doCheckPermission.cpp b/src/doCheckPermission.cpp new file mode 100644 index 0000000..880cce3 --- /dev/null +++ b/src/doCheckPermission.cpp @@ -0,0 +1,79 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(required); + int required_real=ParseInt(required); + if(required_real<0) + { + jsonfail(err_parameter,"Failed to parse permission level"); + break; + } + + startdb(); + + /// Get Permission + int xval=-1; + if(conn.exec(make_str("select permission_level from bs_user where username='", + se.getUser(), + "'"), + SQLParseInt(xval))<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + if(xval<0) + { + jsonfail(err_data,"Failed to parse X-Val"); + break; + } + + if(xval>required_real) + { + jsonfail(err_permission_denied,"Permission Not Reach Required Level"); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doGetBookByObj.cpp b/src/doGetBookByObj.cpp new file mode 100644 index 0000000..a0f458c --- /dev/null +++ b/src/doGetBookByObj.cpp @@ -0,0 +1,70 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(bid); + int bid_real=ParseInt(bid); + if(bid_real<0) + { + jsonfail(err_parameter,"Failed to parse BID"); + break; + } + + startdb(); + + string book_name; + if(conn.exec(make_str("select name from bs_book where class_id in (select class_id from bs_bookstatus where book_id=",bid_real,")"), + [&](MySQLResult& res) + { + res.stepRow([&](char** val,unsigned long* len) + { + j["book_name"]=string(len[0]>0?val[0]:"(NULL)"); + }); + })<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doGetBookStatus.cpp b/src/doGetBookStatus.cpp new file mode 100644 index 0000000..230e4a9 --- /dev/null +++ b/src/doGetBookStatus.cpp @@ -0,0 +1,81 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(id); + + int id_real=ParseInt(id); + if(id_real<0) + { + jsonfail(err_parameter,"Failed to Parse ID"); + break; + } + + startdb(); + + if(conn.exec("set names utf8",nullptr)<0) + { + jsonfail(err_sql,"Failed to set names"); + break; + } + + if(conn.exec(make_str("select book_id,position,status from bs_bookstatus where class_id=",id_real), + [&](MySQLResult& res) + { + res.stepRow([&](char** val,unsigned long* len) + { + json x; + x["obj_id"]=string(val[0]); + x["book_pos"]=string(len[1]>0?val[1]:""); + x["book_status"]=string(val[2]); + + j["result"].push_back(x); + }); + })<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doGetBookTypes.cpp b/src/doGetBookTypes.cpp new file mode 100644 index 0000000..98ddd42 --- /dev/null +++ b/src/doGetBookTypes.cpp @@ -0,0 +1,63 @@ +#include "bs_util.h" +#include +using namespace std; + + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + startdb(); + + if(conn.exec("select book_type from bs_booktype", + [&](MySQLResult& res) + { + res.stepRow([&](char** val,unsigned long* len) + { + j["result"].push_back(string(len[0]>0?val[0]:"")); + }); + })<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doListBook.cpp b/src/doListBook.cpp new file mode 100644 index 0000000..bb17ffb --- /dev/null +++ b/src/doListBook.cpp @@ -0,0 +1,88 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(startid); + postval(amount); + + int startid_real=ParseInt(startid); + if(startid_real<0) + { + jsonfail(err_parameter,"Failed to parse startid"); + break; + } + + int amount_real=ParseInt(amount); + if(amount_real<0) + { + jsonfail(err_parameter,"Failed to parse amount"); + break; + } + + /// Limit to 50. + if(amount_real>50) amount_real=50; + + startdb(); + + if(conn.exec(make_str("select class_id,name,book_type from bs_book where class_id>=", + startid_real, + " order by class_id limit ", + amount_real), + [&](MySQLResult& res) + { + res.stepRow([&](char** val,unsigned long* len) + { + json s; + s["class_id"]=string(val[0]); + s["book_name"]=string(val[1]); + s["book_type"]=string(val[2]); + j["result"].push_back(s); + }); + })<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/login.cpp b/src/doLogin.cpp similarity index 94% rename from src/login.cpp rename to src/doLogin.cpp index 4171f69..e43f0c5 100644 --- a/src/login.cpp +++ b/src/doLogin.cpp @@ -1,6 +1,4 @@ #include "bs_util.h" -#include -#include using namespace std; int main() @@ -27,7 +25,7 @@ int main() { /// Logged in... j["success"]=2; - j["next_url"]="/booksys/dashboard.html"; + j["next_url"]="/booksys/mainpage.html"; break; } @@ -102,7 +100,7 @@ int main() else { j["success"]=1; - j["next_url"]="/booksys/dashboard.html"; + j["next_url"]="/booksys/mainpage.html"; } } while(0); diff --git a/src/doLogout.cpp b/src/doLogout.cpp new file mode 100644 index 0000000..5942f27 --- /dev/null +++ b/src/doLogout.cpp @@ -0,0 +1,48 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + /// Not logged in + jsonfail(err_need_login); + break; + } + + int ret; + if((ret=se.setUser(""))<0) + { + jsonfail(err_session,make_str("Failed to reset session, SessionModule returns: ",ret)); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doRemoveBook.cpp b/src/doRemoveBook.cpp new file mode 100644 index 0000000..3ddd2b8 --- /dev/null +++ b/src/doRemoveBook.cpp @@ -0,0 +1,124 @@ +#include "bs_util.h" +#include "MySQLTransaction.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(id); + int id_real=ParseInt(id); + if(id_real<0) + { + jsonfail(err_parameter,"Failed to parse id"); + break; + } + + startdb(); + + Transaction ts(conn); + + /// Check Permission + int permission_level=-1; + if(conn.exec(make_str("select permission_level from bs_user where username='", + se.getUser(), + "'"), + SQLParseInt(permission_level))<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + if(permission_level<0) + { + jsonfail(err_general,"Failed to check Permission"); + break; + } + + if(permission_level>=3) + { + jsonfail(err_permission_denied); + break; + } + + /// Check if the book info can be removed. + /// count_val: Count of books with class_id={ID} and status is borrowed (0) + int count_val=-1; + if(conn.exec(make_str("select count(book_id) from bs_bookstatus where class_id=", + id_real, + " and status=0"), + SQLParseInt(count_val))<0) + { + jsonfail(err_sql,"Step 2"); + break; + } + + if(count_val<0) + { + jsonfail(err_data,"Failed to get book id data"); + break; + } + + if(count_val!=0) + { + jsonfail(err_data,make_str("Cannot remove book. Still ",count_val," books borrowed.")); + break; + } + + if(conn.exec(make_str("delete from bs_borrow where book_id in (select book_id from bs_bookstatus where class_id=",id_real),nullptr)<0) + { + jsonfail(err_sql,"Step 3"); + } + + if(conn.exec(make_str("delete from bs_bookstatus where class_id=",id_real),nullptr)<0) + { + jsonfail(err_sql,"Step 4"); + break; + } + + if(conn.exec(make_str("delete from bs_book where class_id=",id_real),nullptr)<0) + { + jsonfail(err_sql,"Step 5"); + break; + } + + ts.commit(); + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/doSearch.cpp b/src/doSearch.cpp new file mode 100644 index 0000000..ff258bb --- /dev/null +++ b/src/doSearch.cpp @@ -0,0 +1,73 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(name); + + startdb(); + + if(conn.exec("set names utf8",nullptr)<0) + { + jsonfail(err_sql,"Failed to set names"); + break; + } + + if(conn.exec(make_str("select class_id,name,book_type from bs_book where name like '%", + name, + "%'"), + [&](MySQLResult& res) + { + res.stepRow([&](char** val,unsigned long* len) + { + json s; + s["class_id"]=string(val[0]); + s["book_name"]=string(val[1]); + s["book_type"]=string(val[2]); + j["result"].push_back(s); + }); + })<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} diff --git a/src/doViewBook.cpp b/src/doViewBook.cpp new file mode 100644 index 0000000..3b45b4c --- /dev/null +++ b/src/doViewBook.cpp @@ -0,0 +1,80 @@ +#include "bs_util.h" +using namespace std; + +int main() +{ + Request req; + Session se(req); + Response res; + json j; + + auto jsonfail=[&](int errcode,const std::string& dtl="") + { + jsonfail_func(j,errcode,dtl); + }; + + do + { + if(!se.isReady()) + { + jsonfail(err_session); + break; + } + + if(se.getUser().empty()) + { + jsonfail(err_need_login); + break; + } + + if(req.requestMethod!="POST") + { + jsonfail(err_method_not_supported); + break; + } + + postval(id); + + int id_real=ParseInt(id); + if(id_real<0) + { + jsonfail(err_parameter,"Failed to Parse ID"); + break; + } + + startdb(); + + if(conn.exec(make_str("select name,isbn,book_type,author,publisher,publish_time,status from bs_book where class_id=", + id), + [&](MySQLResult& res) + { + res.stepRow([&](char** val,unsigned long* len) + { + /// define ref + json& x=j; + + x["book_name"]=string(val[0]); + x["book_isbn"]=string(val[1]); + x["book_type"]=string(val[2]); + x["book_author"]=string(val[3]); + x["book_publisher"]=string(val[4]); + x["book_pubdate"]=string(val[5]); + x["book_status"]=ParseInt(val[6]); + }); + })<0) + { + jsonfail(err_sql,"Step 1"); + break; + } + + j["success"]=1; + } + while(0); + + se.writeToResponse(res); + res.content.append(j.dump()); + + return 0; +} + + diff --git a/src/search.cpp b/src/search.cpp deleted file mode 100644 index c94b20c..0000000 --- a/src/search.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "Request.h" -#include "Response.h" -#include "Util.h" -#include "json.hpp" -using namespace std; -using json=nlohmann::json; - -int main() -{ - Request req; - Response res; - json j; - - auto jsonfail=[&](int errcode,const std::string& errmsg) - { - j["success"]=0; - j["errcode"]=errcode; - j["errmsg"]=errmsg; - }; - - if(req.requestMethod=="GET") - { - if(req.get["type"].empty()) - { - jsonfail(2,"Missing Parameter: type"); - } - else - { - string searchType=req.get["type"]; - if(searchType=="0") - { - /// Search User - if(req.get["name"].empty()) - { - jsonfail(3,"Missing Parameter: name"); - } - else - { - /// Search User by ONLY nickname. - string nickname=req.get["name"]; - DBInfo db; - if(db.readConfig()<0) - { - /// Failed to read config. May be not installed. - jsonfail(4,"Failed to read configure."); - } - else - { - MySQLConn conn; - if(db.connectProxy(conn)<0) - { - jsonfail(5,"Failed to connect DB"); - } - else - { - vector> vec; - if(conn.exec(make_str("select name from bs_user where nickname like '%",nickname,"%'"),[&](MySQLResult& res) - { - res.stepRow([&](char** val,unsigned long* len) - { - vec.push_back(make_pair(val[0],val[1])); - }); - })<0) - { - jsonfail(6,"Failed to execute SQL."); - } - else - { - j["success"]=1; - int sz=vec.size(); - for(int i=0;i> vec; - if(conn.exec(make_str("select class_id,name from bs_book where name like '%",bookname,"%'"),[&](MySQLResult& res) - { - res.stepRow([&](char** val,unsigned long* len) - { - vec.push_back(make_pair(val[0],val[1])); - }); - })<0) - { - jsonfail(16,"Failed to execute SQL."); - } - else - { - j["success"]=1; - int sz=vec.size(); - for(int i=0;i + + + BookSystem 添加图书 + {{Header}} + {{navcss}} + + + + {{nav}} +
+ +

添加图书

+

ISBN:

+

图书种类: 添加或删除图书种类

+

图书名称:

+

作者:

+

出版商:

+

出版日期:

+

状态: + +

+ + +
+ + + + \ No newline at end of file diff --git a/web/booksys/addbookobj.html b/web/booksys/addbookobj.html new file mode 100644 index 0000000..cc1bac5 --- /dev/null +++ b/web/booksys/addbookobj.html @@ -0,0 +1,104 @@ + + + + BookSystem 添加图书实体 + {{Header}} + {{navcss}} + + + + {{nav}} +
+
+ +
+

添加图书实体

+

图书标题:【正在获取图书信息...】么?

+

存放位置:

+

状态: + +

+ + + +
+
+ + + + \ No newline at end of file diff --git a/web/booksys/borrow-success.html b/web/booksys/borrow-success.html new file mode 100644 index 0000000..b4e302d --- /dev/null +++ b/web/booksys/borrow-success.html @@ -0,0 +1,27 @@ + + + + BookSystem 图书借阅成功 + {{Header}} + {{navcss}} + + + + {{nav}} +
+

借阅成功!请及时到线下图书馆取走书籍

+ +
+ + + + \ No newline at end of file diff --git a/web/booksys/borrowbook.html b/web/booksys/borrowbook.html new file mode 100644 index 0000000..9ec0859 --- /dev/null +++ b/web/booksys/borrowbook.html @@ -0,0 +1,71 @@ + + + + BookSystem 图书借阅 + {{Header}} + {{navcss}} + + + + {{nav}} +
+

确认借阅: 【正在获取图书信息】 么?

+ + +
+ + + + \ No newline at end of file diff --git a/web/booksys/img/default_user.png b/web/booksys/img/default_user.png index 98bb652d67fc2f631e14e30bbc93f241705b1d7a..d851dcc5b3f6b297409ef59b0ca630588181ac31 100644 GIT binary patch literal 2168 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 diff --git a/web/booksys/img/search_icon.png b/web/booksys/img/search_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cd3034ae7d0198ce674d6131527a8c3421c83ea9 GIT binary patch literal 2204 zcmbuB`BxL?8ppE{Vw4c1fPo-sQ;JsiwFr6iBRIj zT0|TmN)<&7f?+whC18ynFkz8J$`YU?EDA(GDuVP)|ABk&xj)Rj=XuZf{XEM&&&-?; z4rH4Ut%)cU%EaGq^LDUyf$J>+1MUU;oISyUhPSgfqNFP72rzKRSld`ARQZLa^M{Op zy@cl%3ZqcVPZ=(BZ@67N3Po`7-^|(>f8bsF9~TFi%W=&g7mLQ*KDeJ;9#H!Aukrpz z<`Oms+kd$ts(o=jr-di}{u#8|Om}NDQoNl1zZ#ipeNoUPrKjYzN7yc9|HSC(1~(N? zrJNnvdZ((FG~@KkJ?3ziPVUkREF z{${?yA>NN0u#+;uGYvZOds%nAd16rJlla&(@rRA(2fRG5?H=c5x>aR3v@0fh>&@qy zUv1D!8KRD7whYlscY&=wmm%8yeA|b~pFVWAlDOe%HN@yj*4=EI(*A;zb1dUfLeTjT@6I8KQu}QrB-yX4N@&(N3Q9)`J~wtOYd7p*zXHr@x7}iGm?4v))k4)kfd2tT5{!{OEz#3Uq|5M&u*Olq<;k6w}}zKIX~1bx=7w-NTNw;Xk{fn9|v%E_s9lyZ4GE7V|ix9h1rkMCc`R zxE>Rvir<+&;!JCD7GG^!k&yNw>W<rw((VAGdVl^@;m+KdV{qLeQR_ld6eb-(IBr>d%|Bu*`IZ_k}*`=fYa{ZOC zbrj)jHx#7DW%joR!W|2iUU*y^r2Ki9uaTj3d1hWDgu(0lAtG9{v#3}hkbpgs2?_bY zYp2=~rkB(%B_uN6S53R|N`snu16OPg-^P-O(`RFk<3SNo@=gO|+ zRECV!J*pp^j4P|F#^a*Cc)E@|1Ms`%F9{?Wrx!oJ=bS^cuwVokO!^%a6#fixRnh9jNLsYBh zz;$GdIFgfwR^%gt?}5+zG+m7+p#7K)-xU~S1JtZAK#OH6-9d!+NxD5m*JT9DerkZh zi3L-6qEqwp8ZX@*GJp?hsgZ}%(5z@x`I_(*@@0OLkK)y9CPA{=Ls?F$7|Rlf|IibFVTkVef__^k*q zqKBwg4d_oy~~>+@e(Qi;8HKpY*mGP0y(~2uL=n==pm#y zxeip$fhSHBn&5GUH5)t+HVjq}8fl0+V+l&=A%felYRCa z5I)gTXIwyFNw1($x4eG&NCzOFx1UR4|1d=TRRr(+N)>XR$+jQ(e~gIvk5hK~*ut0G U#xrZd9|g*POW 安装成功 - - - - - - + {{Header}} diff --git a/web/booksys/install.html b/web/booksys/install.html index 2cebf4b..d28c02f 100644 --- a/web/booksys/install.html +++ b/web/booksys/install.html @@ -2,12 +2,7 @@ BookSystem 安装 - - - - - - + {{Header}}