当代数据库管理系统课程实验二
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

722 líneas
24 KiB

hace 1 año
hace 1 año
hace 1 año
hace 1 año
hace 1 año
hace 1 año
hace 1 año
hace 1 año
hace 1 año
hace 1 año
hace 1 año
  1. ## <center> 华东师范大学数据科学与工程学院实验报告
  2. | **课程名称:当代数据库管理系统** |**年级** :2020级 |**上机实践成绩**: |
  3. | --- | --- | --- |
  4. | **指导教师** :周烜 | **姓名** :杨舜、姚嘉和 | **学号** :10205501415、10205501436 |
  5. | **上机实践名称** :BookStore ||**上机实践日期**:2022.11.28 —— 2022.12.10 |
  6. | **上机实践编号** : | **组号 :21** |**上机实践时间**:2022.11.28 —— 2022.12.10 |
  7. ### 实验过程
  8. #### 一. 分析原有的数据库结构
  9. 分析demo中/be/model/store.py中创建数据库表的sql语句可知原有数据库的结构的ER图大致如下
  10. ![avatar](./figure_require/DB_struct_origin.svg)
  11. 有上述ER图可以得到原有数据库表如下
  12. user表:
  13. |user_id|password|balance|token|terminal|
  14. |---|---|---|---|---|
  15. 主键为user_id
  16. store表:
  17. |store_id|stock_level|
  18. |---|---|
  19. 主键为store_id
  20. store_book表:
  21. |store_id|book_id|book_info|stock_level|
  22. |---|---|---|---|
  23. 主键为联合主键(store_id,book_id)
  24. user_store表:
  25. |user_id|store_id|
  26. |---|---|
  27. 外键为(user_id,store_id)
  28. new_order表:
  29. |order_id|user_id|store_id|
  30. |---|---|---|
  31. 主键为(order_id)
  32. new_order_detail表:
  33. |oeder_id|book_id|count|price|
  34. |---|---|---|---|
  35. 主键为联合主键(order_id,book_id)
  36. &nbsp;
  37. #### 二. 依据上述分析构建数据库table(前60%)
  38. 1. 利用sqlalchemy连接远程的aliyun数据库并创建上述table [postgreSQLORM.py](./modified/be/postgreSQLORM.py)
  39. ```python
  40. class con:
  41. def connect():
  42. '''Returns a connection and a metadata object'''
  43. # We connect with the help of the PostgreSQL URL
  44. url = 'postgresql://stu10205501415:Stu10205501415@dase-cdms-2022-pub.pg.rds.aliyuncs.com:5432/stu10205501415'
  45. # The return value of create_engine() is our connection object
  46. con = create_engine(url, client_encoding='utf8')
  47. # We then bind the connection to MetaData()
  48. meta = MetaData(bind=con)
  49. return con, meta
  50. class User(Base):
  51. __tablename__ = 'user'
  52. user_id = Column(TEXT, primary_key=True, comment="主键")
  53. password = Column(TEXT, nullable=False, comment="密码")
  54. balance = Column(Integer, nullable=False, comment="")
  55. token = Column(TEXT, comment="缓存的令牌")
  56. terminal = Column(TEXT, comment="终端代码")
  57. class Store(Base):
  58. __tablename__ = 'store'
  59. store_id = Column(TEXT, primary_key=True,comment="主键")
  60. stock_level = Column(Integer, comment = "货存")
  61. class Store_Book(Base):
  62. __tablename__ = 'store_book'
  63. store_id = Column(TEXT, comment="主键")
  64. book_id = Column(TEXT, comment="主键")
  65. book_info = Column(TEXT, comment="书籍信息")
  66. stock_level = Column(Integer, comment = "货存")
  67. __table_args__ = (
  68. PrimaryKeyConstraint('store_id', 'book_id'),
  69. )
  70. class User_Store(Base):
  71. __tablename__ = 'user_store'
  72. id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
  73. fk_user_id = Column(
  74. TEXT,
  75. ForeignKey(
  76. "user.user_id",
  77. ondelete="CASCADE",
  78. onupdate="CASCADE",
  79. ),
  80. nullable=False,
  81. comment="user外键"
  82. )
  83. fk_store_id = Column(
  84. TEXT,
  85. ForeignKey(
  86. "store.store_id",
  87. ondelete="CASCADE",
  88. onupdate="CASCADE",
  89. ),
  90. nullable=False,
  91. comment="store外键"
  92. )
  93. # 多对多关系的中间表必须使用联合唯一约束,防止出现重复数据
  94. __table_args__ = (
  95. UniqueConstraint("fk_user_id", "fk_store_id"),
  96. )
  97. class New_Order(Base):
  98. __tablename__ = 'new_order'
  99. order_id = Column(TEXT, primary_key = True, comment = '订单id')
  100. fk_user_id = Column(
  101. TEXT,
  102. ForeignKey(
  103. "user.user_id",
  104. ondelete="CASCADE",
  105. onupdate="CASCADE",
  106. ),
  107. nullable=False,
  108. comment="user外键"
  109. )
  110. fk_store_id = Column(
  111. TEXT,
  112. ForeignKey(
  113. "store.store_id",
  114. ondelete="CASCADE",
  115. onupdate="CASCADE",
  116. ),
  117. nullable=False,
  118. comment="store外键"
  119. )
  120. class New_Order_Detail(Base):
  121. __tablename__ = 'new_order_detail'
  122. order_id = Column(TEXT, comment='订单id')
  123. book_id = Column(TEXT, comment='订单书籍')
  124. count = Column(Integer, comment='购买书籍数')
  125. price = Column(Integer, comment='单价')
  126. __table_args__ = (
  127. PrimaryKeyConstraint('order_id','book_id'),
  128. )
  129. engine, meta = con.connect()
  130. Base.metadata.bind = engine
  131. DBSession = sessionmaker(bind=engine)
  132. session = DBSession()
  133. ```
  134. 2. 在上述创建的table中添加初始数据并利用该数据测试后端服务器与数据库的连接(2022.11.29 15:10 杨舜)
  135. ![avatar](./figure_require/add_naive_userdata.png)
  136. ![avatar](./figure_require/conn_test.png)
  137. 3. 类比原有demo分别为不同的路由绑定不同的蓝图
  138. ```python
  139. app.register_blueprint(auth.bp_auth)
  140. app.register_blueprint(seller.bp_seller)
  141. app.register_blueprint(buyer.bp_buyer)
  142. ```
  143. 4. 在/be/model目录下创建User类用书实现User对于数据库的一些交互功能
  144. 5. 修改[model/db_conn.py](./be/model/db_conn.py)中查询的操作为orm操作,其中修改DBConn中的连接conn为sqlachlemy中的session,将会话作为连接
  145. ```python
  146. class DBConn:
  147. def __init__(self):
  148. self.session = postgreSQLORM.session
  149. return
  150. # self.conn = store.get_db_conn()
  151. def user_id_exist(self, user_id):
  152. row = self.session.query(User).filter(User.user_id==user_id).first()
  153. # cursor = self.conn.execute("SELECT user_id FROM user WHERE user_id = ?;", (user_id,))
  154. # row = cursor.fetchone()
  155. if row is None:
  156. return False
  157. else:
  158. return True
  159. def book_id_exist(self, store_id, book_id):
  160. row = self.session.query(Store_Book).filter(Store_Book.book_id==book_id and Store_Book.store_id==store_id).first()
  161. # cursor = self.conn.execute("SELECT book_id FROM store WHERE store_id = ? AND book_id = ?;", (store_id, book_id))
  162. # row = cursor.fetchone()
  163. if row is None:
  164. return False
  165. else:
  166. return True
  167. def store_id_exist(self, store_id):
  168. row = self.session.query(User_Store).filter(User_Store.fk_store_id==store_id).first()
  169. # cursor = self.conn.execute("SELECT store_id FROM user_store WHERE store_id = ?;", (store_id,))
  170. # row = cursor.fetchone()
  171. if row is None:
  172. return False
  173. else:
  174. return True
  175. ```
  176. 6. 修改/be/model/user.py中User类以及其中函数的定义使其满足题目要求的ORM模型,同时仅对注册功能进行测试(2022.11.30 15:40 杨舜)
  177. ```python
  178. class User(db_conn.DBConn):
  179. token_lifetime: int = 3600 # 3600 second
  180. def __init__(self):
  181. db_conn.DBConn.__init__(self)
  182. def __check_token(self, user_id, db_token, token) -> bool:
  183. try:
  184. if db_token != token:
  185. return False
  186. jwt_text = jwt_decode(encoded_token=token, user_id=user_id)
  187. ts = jwt_text["timestamp"]
  188. if ts is not None:
  189. now = time.time()
  190. if self.token_lifetime > now - ts >= 0:
  191. return True
  192. except jwt.exceptions.InvalidSignatureError as e:
  193. logging.error(str(e))
  194. return False
  195. def register(self, user_id: str, password: str):
  196. ## 判断用户是否注册过了
  197. if self.user_id_exist(user_id=user_id):
  198. return error.error_exist_user_id(user_id)
  199. else:
  200. # try:
  201. terminal = "terminal_{}".format(str(time.time()))
  202. token = jwt_encode(user_id, terminal)
  203. ## 为新注册的用户创建对象
  204. new_user = postgreSQLORM.User(user_id=user_id,password=password,balance=0,token=token,terminal=terminal)
  205. self.session.add(new_user)
  206. self.session.commit()
  207. # self.conn.execute(
  208. # "INSERT into user(user_id, password, balance, token, terminal) "
  209. # "VALUES (?, ?, ?, ?, ?);",
  210. # (user_id, password, 0, token, terminal), )
  211. # self.conn.commit()
  212. # except sqlite.Error:
  213. # return error.error_exist_user_id(user_id)
  214. return 200, "ok"
  215. ```
  216. ![avatar](./figure_require/register_test.png)
  217. 7. 另外对于auth路由中的其他功能接口(注销、登录、登出、更改密码)进行类似上述注册接口的修改,此处不在单独贴出代码,只是给出postman的测试截图,至此auth中的路由全部实现(2022.11.30 17:50 杨舜)
  218. (1)注册接口思路:
  219. 首先查找库中的user表对用户输入的id查重之后根据terminal和id生成一个token,之后为用户建立一个新对象并插入到表user中
  220. (2)登陆接口思路:
  221. 首先调用check_password函数对用户输入的密码进行判断得到code和message,先对code判断密码是否正确,之后根据不同的错误返回相应的不同的code和message并返回,若无错误则code返回200,message返回ok
  222. (3)登出接口思路:
  223. 根据id查看是否已登录,之后更新token和terminal
  224. (4)注销接口思路:
  225. 通过check_password函数检查该用户是否已存在账户,之后从表中删除该条目
  226. (5)更改密码接口思路:
  227. 首先根据id获取用户的旧密码,与用户输入的旧密码对比,若符合则更新新密码
  228. ![avatar](./figure_require/unregister_test.png)
  229. ![avatar](./figure_require/login_test.png)
  230. ![avatar](./figure_require/logout_test.png)
  231. ![avatar](./figure_require/password_test.png)
  232. 8. 利用上述类似的实现auth路由接口的方式完成seller路由接口,并利用postman测试实现(2022.12.01 19:20 杨舜)
  233. ![avatar](./figure_require/creatstore_test.png)
  234. ![avatar](./figure_require/addbook_test.png)
  235. ![avatar](./figure_require/addstocklevel_test.png)
  236. 9. 利用上述类似的实现auth路由接口的方式完成buyer路由接口,并利用postman测试实现(2022.12.02 12:10 杨舜)
  237. ![avatar](./figure_require/neworder_test.png)
  238. ![avatar](./figure_require/addfunds_test.png)
  239. ![avatar](./figure_require/payment_test.png)
  240. #### 三、根据要求实现后续的40%的功能并为其编写测试接口
  241. 1. 对数据库结构进行改造(添加table的列)
  242. 为了实现发货,收获,订单状态的查询,可以在new_order的订单的table中添加status,并利用不同的状态码来表示当前次订单的状态
  243. |status code|status|
  244. |---|---|
  245. |-1|取消|
  246. |0|初始值(未付款)|
  247. |1|已付款|
  248. |2|已发货|
  249. |3|已收货|
  250. 因此修改postgreSQLORM.py文件中New_Order的类
  251. 2. 修改对应payment接口中删除订单的操作未修改订单状态未1
  252. ```python
  253. row = session.query(New_Order).filter(New_Order.order_id==order_id).update({'status':1})
  254. ```
  255. 3. 为seller路由新增发货(修改订单状态为2)接口、buyer路由新增收货(修改订单状态为3)接口、buyer路由新增取消订单(修改订单状态为-1)接口
  256. ```python
  257. ## /view/seller.py
  258. @bp_seller.routr("/send_out",methods=["POST"])
  259. def send_out():
  260. order_id: str = request.json.get("order_id")
  261. user_id: str = request.json.get("user_id")
  262. s = seller.Seller()
  263. code, message = s.send_out(order_id)
  264. return jsonify({"message": message}), code
  265. ```
  266. ```python
  267. ## /model/seller.py
  268. def send_out(self, order_id:str):
  269. session = self.session
  270. try:
  271. if not self.user_id_exist(user_id):
  272. return error.error_non_exist_user_id(user_id)
  273. row = session.query(New_Order).filter(New_Order.order_id==order_id).first()
  274. if row is None:
  275. return error.error_invalid_order_id(order_id)
  276. if row.status != 1:
  277. return error.error_invalid_order_id(order_id)
  278. row = session.query(New_Order).filter(New_Order.order_id==order_id).update({'status':2})
  279. if row == 0:
  280. return error.error_invalid_order_id(order_id)
  281. session.commit()
  282. except SQLAlchemyError as e:
  283. return 528, "{}".format(str(e))
  284. except BaseException as e:
  285. # print('touch3')
  286. return 530, "{}".format(str(e))
  287. return 200, "ok"
  288. ```
  289. (1)创建商铺接口实现:
  290. 首先检查user_id和store_id是否已存在,若未存在则创建新对象,并插入表user_store中
  291. (2)添加书本信息接口实现:
  292. 首先检查user_id和store_id是否存在,再检查book_id是否已有对应的书本,最后将书本信息创建新对象插入表store_book中
  293. (3)添加库存接口实现:
  294. 首先检查user_id和store_id和book_id是否存在,之后根据store_id和book_id找到对应店铺的对应书本库存并进行更新
  295. (4)商家发货接口实现:
  296. 首先检查user_id是否存在,之后检查order_id是否有效,对new_order表进行更新
  297. (5)买家下单接口实现:
  298. 首先检查user_id和store_id是否存在,之后检查对应店铺的所要买的书本库存是否足够,若足够则减去对应库存,生成order_id,对表new_order_detail更新
  299. (6)买家充值接口实现:
  300. 首先根据user_id获取对应密码,与用户输入密码对比,相符合则在表user中更新余额
  301. (7)买家付款接口实现:
  302. 首先检查order_id是否存在,若存在则根据user_id获取密码,与用户输入密码对比,之后检查store_id与卖家的user_id是否存在,若存在则比较买家余额和订单价格,若余额足够则扣除买家相应价格,在卖家余额上等额增加,更新user表
  303. (8)买家收货接口实现:
  304. 首先检查user_id是否存在,检查order_id是否有效,之后更新new_order表
  305. (9)买家取消订单接口实现:
  306. 首先检查user_id是否存在,之后根据订单信息查找对应的订单,将查找到的订单从表new_order中删去,更新
  307. (10)买家查看历史订单接口实现:
  308. 首先检查user_id是否存在,之后根据user_id查找表new_order,之后根据order_id列出订单
  309. (11)自动取消订单接口实现:
  310. 首先根据order_id查找表new_order,之后根据当前时间计算订单处于未付款状态的持续时长,若超过30s处于未付款状态则自动删去该条目,更新表new_order
  311. 商家发货
  312. URL
  313. POST http://[address]/seller/send_out
  314. Request
  315. Headers:
  316. key | 类型 | 描述 | 是否可为空
  317. ---|---|---|---
  318. token | string | 登录产生的会话标识 | N
  319. Body:
  320. ```json
  321. {
  322. "user_id": "$seller id$",
  323. "order_id": "$store id$",
  324. }
  325. ```
  326. key | 类型 | 描述 | 是否可为空
  327. ---|---|---|---
  328. user_id | String | 卖家用户ID | N
  329. order_id | String | 订单号 | N
  330. Response
  331. Status Code:
  332. 码 | 描述
  333. --- | ---
  334. 200 | 发货成功
  335. 5XX | 买家用户ID不存在
  336. 5XX | 无效参数
  337. ```python
  338. ## /view/buyer.py
  339. @bp_buyer.route("/take_over", methods=["POST"])
  340. def take_over():
  341. user_id = request.json.get("user_id")
  342. order_id = request.json.get("order_id")
  343. b = Buyer()
  344. code, message = b.take_over(user_id, order_id)
  345. return jsonify({"message": message}), code
  346. ```
  347. ```python
  348. ## /model/buyer.py
  349. def take_over(self, user_id, order_id):
  350. session = self.session
  351. try:
  352. if not self.user_id_exist(user_id):
  353. return error.error_non_exist_user_id(user_id)
  354. row = session.query(New_Order).filter(and_(New_Order.order_id==order_id,New_Order.fk_user_id==user_id)).first()
  355. if row is None:
  356. return error.error_invalid_order_id(order_id)
  357. if row.status != 2:
  358. return error.error_invalid_order_id(order_id)
  359. row = session.query(New_Order).filter(and_(New_Order.order_id==order_id,New_Order.fk_user_id==user_id)).update({'status':3})
  360. if row == 0:
  361. return error.error_invalid_order_id(order_id)
  362. session.commit()
  363. except SQLAlchemyError as e:
  364. return 528, "{}".format(str(e))
  365. except BaseException as e:
  366. # print('touch3')
  367. return 530, "{}".format(str(e))
  368. return 200, "ok"
  369. ```
  370. 卖家收货
  371. URL
  372. POST http://[address]/buyer/take_over
  373. Request
  374. Headers:
  375. key | 类型 | 描述 | 是否可为空
  376. ---|---|---|---
  377. token | string | 登录产生的会话标识 | N
  378. Body:
  379. ```json
  380. {
  381. "user_id": "$seller id$",
  382. "order_id": "$store id$",
  383. }
  384. ```
  385. key | 类型 | 描述 | 是否可为空
  386. ---|---|---|---
  387. user_id | String | 买家用户ID | N
  388. order_id | String | 订单号 | N
  389. Response
  390. Status Code:
  391. 码 | 描述
  392. --- | ---
  393. 200 | 收货成功
  394. 5XX | 买家用户ID不存在
  395. 5XX | 无效参数
  396. ```python
  397. ## /view/buyer.py
  398. @bp_buyer.route("/order_cancel", methods=["POST"])
  399. def take_over():
  400. user_id = request.json.get("user_id")
  401. order_id = request.json.get("order_id")
  402. b = Buyer()
  403. code, message = b.order_cancel(user_id, order_id)
  404. return jsonify({"message": message}), code
  405. ```
  406. ```python
  407. ## /model/buyer.py
  408. def order_cancel(self, user_id, order_id):
  409. session = self.session
  410. try:
  411. if not self.user_id_exist(user_id):
  412. return error.error_non_exist_user_id(user_id)
  413. row = session.query(New_Order).filter(and_(New_Order.order_id==order_id,New_Order.fk_user_id==user_id)).first()
  414. if row is None:
  415. return error.error_invalid_order_id(order_id)
  416. if row.status != 0:
  417. return error.error_invalid_order_id(order_id)
  418. row = session.query(New_Order).filter(and_(New_Order.order_id==order_id,New_Order.fk_user_id==user_id)).update({'status':-1})
  419. if row == 0:
  420. return error.error_invalid_order_id(order_id)
  421. session.commit()
  422. except SQLAlchemyError as e:
  423. return 528, "{}".format(str(e))
  424. except BaseException as e:
  425. # print('touch3')
  426. return 530, "{}".format(str(e))
  427. return 200, "ok"
  428. ```
  429. 买家取消订单
  430. URL
  431. POST http://[address]/buyer/order_cancel
  432. Request
  433. Headers:
  434. key | 类型 | 描述 | 是否可为空
  435. ---|---|---|---
  436. token | string | 登录产生的会话标识 | N
  437. Body:
  438. ```json
  439. {
  440. "user_id": "$seller id$",
  441. "order_id": "$store id$",
  442. }
  443. ```
  444. key | 类型 | 描述 | 是否可为空
  445. ---|---|---|---
  446. user_id | String | 买家用户ID | N
  447. order_id | String | 订单号 | N
  448. Response
  449. Status Code:
  450. 码 | 描述
  451. --- | ---
  452. 200 | 收货成功
  453. 5XX | 买家用户ID不存在
  454. 5XX | 无效参数
  455. 5. 像buyer路由中添加一个查询历史订单的接口,为此首先需要在原来的New_Order表中添加creat_time列,然后为buyer路由添加history_order接口
  456. ```python
  457. ## /view/buyer.py
  458. @bp_buyer.route("/history_order", methods=["POST"])
  459. def take_over():
  460. user_id = request.json.get("user_id")
  461. b = Buyer()
  462. code, message = b.history_order(user_id)
  463. return jsonify({"message": message}), code
  464. ```
  465. ```python
  466. ## /model/buyer.py
  467. ```
  468. 历史订单查询
  469. URL
  470. POST http://[address]/buyer/history_order
  471. Request
  472. Headers:
  473. key | 类型 | 描述 | 是否可为空
  474. ---|---|---|---
  475. token | string | 登录产生的会话标识 | N
  476. Body:
  477. ```json
  478. {
  479. "user_id": "$buyer id$"
  480. }
  481. ```
  482. key | 类型 | 描述 | 是否可为空
  483. ---|---|---|---
  484. user_id | String | 买家用户ID | N
  485. Response
  486. Status Code:
  487. 码 | 描述
  488. --- | ---
  489. 200 | 查询成功
  490. 5XX | 买家用户ID不存在
  491. 5XX | 无效参数
  492. Body:
  493. ```json
  494. {
  495. "order_id": ["uuid"]
  496. }
  497. ```
  498. 变量名 | 类型 | 描述 | 是否可为空
  499. ---|---|---|---
  500. order_id | string | 订单号,只有返回200时才有效 | N
  501. 6. 为上面添加的路由编写测试接口并进行测试
  502. ![avatar](./figure_require/route_test01.png)
  503. 7. 为实现书店的搜索图书的功能,稍微修改数据库的结构,为书籍添加数据表,搜索标题表,搜索标签表,搜索作者表,搜索书本内容表
  504. Book
  505. |book_id|title|author|publisher|original_title|translator|pub_year|pages|original_price|currency_unit|binding|isbn|author_intro|book_intro|content|tags|picture|
  506. |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
  507. 主键(book_id)
  508. Search_title
  509. |search_id|title|book_id|
  510. |---|---|---|
  511. 联合主键(search_id,book_id)
  512. 外键(book_id)
  513. Search_tags
  514. |search_id|tags|book_id|
  515. |---|---|---|
  516. 联合主键(search_id,book_id)
  517. 外键(book_id)
  518. Search_author
  519. |search_id|author|book_id|
  520. |---|---|---|
  521. 联合主键(search_id,author)
  522. 外键(book_id)
  523. Search_book_intro
  524. |search_id|book_intro|book_id|
  525. |---|---|---|
  526. 联合主键(search_id,book_id)
  527. 外键(book_id)
  528. 8. 修改seller中的add_book的路由
  529. 9. 在auth中添加搜索的路由(只包含全局搜索,没有店铺内搜索)
  530. ```python
  531. @bp_auth.route("/search_author", methods=["POST"])
  532. def search_author():
  533. author = request.json.get("author", "")
  534. page = request.json.get("page", "")
  535. u = user.User()
  536. code, message = u.search_author(author=author, page=page)
  537. return jsonify({"message": message}), code
  538. @bp_auth.route("/search_book_intro", methods=["POST"])
  539. def search_book_intro():
  540. book_intro = request.json.get("book_intro", "")
  541. page = request.json.get("page", "")
  542. u = user.User()
  543. code, message = u.search_book_intro(book_intro=book_intro, page=page)
  544. return jsonify({"message": message}), code
  545. @bp_auth.route("/search_tags", methods=["POST"])
  546. def search_tags():
  547. tags = request.json.get("tags", "")
  548. page = request.json.get("page", "")
  549. u = user.User()
  550. code, message = u.search_tags(tags=tags, page=page)
  551. return jsonify({"message": message}), code
  552. @bp_auth.route("/search_title", methods=["POST"])
  553. def search_title():
  554. title = request.json.get("title", "")
  555. page = request.json.get("page", "")
  556. u = user.User()
  557. code, message = u.search_title(title=title, page=page)
  558. return jsonify({"message": message}), code
  559. ```
  560. 10. 为搜索编写测试接口
  561. ![avatar](./figure_require/route_test01.png)
  562. ![avatar](./figure_require/route_test02.png)
  563. ![avatar](./figure_require/route_test03.png)
  564. ### 测试结果
  565. ```bash
  566. bash script/test.sh
  567. ```
  568. 首先针对要实现的功能编写相应test,再进行功能实现,符合测试驱动开发的方法,最后输入上述命令,从上面的图中可以看出绝大部分的测试都能够通过,代码覆盖率为54%,作为核心组件的buyer.py、seller.py和user.py的代码覆盖率均在50%-65%之间
  569. ### 总结
  570. 本次书店project结合了这学期目前为止所学的许多知识,er图、ORM的使用与查询等,是一个比较大的挑战,在本次大项目中在构思方面帮助我们更好的理解和掌握了er图的绘制,在代码方面帮助我们对orm的增删改查操作有了较多的熟悉,同时,由于本次为合作作业,也让我们对git的使用变得更加流畅与顺手,相信在下次的大项目中能有比较大的提升
  571. ### 分工
  572. 在本次项目的实现过程中采用了水杉码园的代码托管工具用于版本管理
  573. 下图为项目仓库的提交图:
  574. ![avatar](./figure_require/git_require.png)
  575. 其中项目的参与人如下:
  576. 10205501415 —— 杨舜
  577. Max —————— 姚嘉和
  578. 项目的链接为
  579. https://gitea.shuishan.net.cn/10205501415/BookStore-PJ2
  580. 具体分工:
  581. - 杨舜:数据库框架设计、数据库实现、数据库整合、路由接口编写、路由代码本机测试、测试接口代码设计与实现、git版本控制、报告撰写
  582. - 姚嘉和:数据库实现、路由接口编写、路由代码本机测试、测试接口代码参与编写、接口功能测试、报告撰写