:sparkles: Member Infos
huangqimin 6 年之前
父节点
当前提交
3d0d214667
共有 297 个文件被更改,包括 967 次插入17 次删除
  1. 1 1
      account/admin.py
  2. 35 0
      account/migrations/0037_auto_20191119_1348.py
  3. 27 0
      account/models.py
  4. 200 0
      api/member_views.py
  5. 21 2
      api/urls.py
  6. 25 0
      integral/migrations/0011_auto_20191119_1348.py
  7. 21 0
      kodo/settings.py
  8. 8 1
      mch/admin.py
  9. 36 0
      mch/migrations/0039_auto_20191119_1348.py
  10. 21 0
      mch/models.py
  11. 0 0
      member/__init__.py
  12. 38 0
      member/admin.py
  13. 8 0
      member/apps.py
  14. 104 0
      member/migrations/0001_initial.py
  15. 0 0
      member/migrations/__init__.py
  16. 211 0
      member/models.py
  17. 7 0
      member/tests.py
  18. 7 0
      member/views.py
  19. 1 1
      pre/views.py
  20. 4 4
      requirements.txt
  21. 6 6
      requirements_dj.txt
  22. 1 1
      requirements_pywe.txt
  23. 1 1
      requirements_redis.txt
  24. 0 0
      simditor/__init__.py
  25. 36 0
      simditor/fields.py
  26. 0 0
      simditor/image/__init__.py
  27. 11 0
      simditor/image/dummy_backend.py
  28. 28 0
      simditor/image/pillow_backend.py
  29. 16 0
      simditor/image_processing.py
  30. 二进制
      simditor/static/simditor/fonts/icomoon.eot
  31. 11 0
      simditor/static/simditor/fonts/icomoon.svg
  32. 二进制
      simditor/static/simditor/fonts/icomoon.ttf
  33. 二进制
      simditor/static/simditor/fonts/icomoon.woff
  34. 82 0
      simditor/static/simditor/fonts/selection.json
  35. 二进制
      simditor/static/simditor/images/emoji/+1.png
  36. 二进制
      simditor/static/simditor/images/emoji/-1.png
  37. 二进制
      simditor/static/simditor/images/emoji/100.png
  38. 二进制
      simditor/static/simditor/images/emoji/109.png
  39. 二进制
      simditor/static/simditor/images/emoji/1234.png
  40. 二进制
      simditor/static/simditor/images/emoji/a.png
  41. 二进制
      simditor/static/simditor/images/emoji/ab.png
  42. 二进制
      simditor/static/simditor/images/emoji/abc.png
  43. 二进制
      simditor/static/simditor/images/emoji/abcd.png
  44. 二进制
      simditor/static/simditor/images/emoji/accept.png
  45. 二进制
      simditor/static/simditor/images/emoji/aerial_tramway.png
  46. 二进制
      simditor/static/simditor/images/emoji/airplane.png
  47. 二进制
      simditor/static/simditor/images/emoji/alarm_clock.png
  48. 二进制
      simditor/static/simditor/images/emoji/alien.png
  49. 二进制
      simditor/static/simditor/images/emoji/ambulance.png
  50. 二进制
      simditor/static/simditor/images/emoji/anchor.png
  51. 二进制
      simditor/static/simditor/images/emoji/angel.png
  52. 二进制
      simditor/static/simditor/images/emoji/anger.png
  53. 二进制
      simditor/static/simditor/images/emoji/angry.png
  54. 二进制
      simditor/static/simditor/images/emoji/anguished.png
  55. 二进制
      simditor/static/simditor/images/emoji/ant.png
  56. 二进制
      simditor/static/simditor/images/emoji/apple.png
  57. 二进制
      simditor/static/simditor/images/emoji/aquarius.png
  58. 二进制
      simditor/static/simditor/images/emoji/aries.png
  59. 二进制
      simditor/static/simditor/images/emoji/arrow_backward.png
  60. 二进制
      simditor/static/simditor/images/emoji/arrow_double_down.png
  61. 二进制
      simditor/static/simditor/images/emoji/arrow_double_up.png
  62. 二进制
      simditor/static/simditor/images/emoji/arrow_down.png
  63. 二进制
      simditor/static/simditor/images/emoji/arrow_down_small.png
  64. 二进制
      simditor/static/simditor/images/emoji/arrow_forward.png
  65. 二进制
      simditor/static/simditor/images/emoji/arrow_heading_down.png
  66. 二进制
      simditor/static/simditor/images/emoji/arrow_heading_up.png
  67. 二进制
      simditor/static/simditor/images/emoji/arrow_left.png
  68. 二进制
      simditor/static/simditor/images/emoji/arrow_lower_left.png
  69. 二进制
      simditor/static/simditor/images/emoji/arrow_lower_right.png
  70. 二进制
      simditor/static/simditor/images/emoji/arrow_right.png
  71. 二进制
      simditor/static/simditor/images/emoji/arrow_right_hook.png
  72. 二进制
      simditor/static/simditor/images/emoji/arrow_up.png
  73. 二进制
      simditor/static/simditor/images/emoji/arrow_up_down.png
  74. 二进制
      simditor/static/simditor/images/emoji/arrow_up_small.png
  75. 二进制
      simditor/static/simditor/images/emoji/arrow_upper_left.png
  76. 二进制
      simditor/static/simditor/images/emoji/arrow_upper_right.png
  77. 二进制
      simditor/static/simditor/images/emoji/arrows_clockwise.png
  78. 二进制
      simditor/static/simditor/images/emoji/arrows_counterclockwise.png
  79. 二进制
      simditor/static/simditor/images/emoji/art.png
  80. 二进制
      simditor/static/simditor/images/emoji/articulated_lorry.png
  81. 二进制
      simditor/static/simditor/images/emoji/astonished.png
  82. 二进制
      simditor/static/simditor/images/emoji/atm.png
  83. 二进制
      simditor/static/simditor/images/emoji/b.png
  84. 二进制
      simditor/static/simditor/images/emoji/baby.png
  85. 二进制
      simditor/static/simditor/images/emoji/baby_bottle.png
  86. 二进制
      simditor/static/simditor/images/emoji/baby_chick.png
  87. 二进制
      simditor/static/simditor/images/emoji/baby_symbol.png
  88. 二进制
      simditor/static/simditor/images/emoji/baggage_claim.png
  89. 二进制
      simditor/static/simditor/images/emoji/balloon.png
  90. 二进制
      simditor/static/simditor/images/emoji/ballot_box_with_check.png
  91. 二进制
      simditor/static/simditor/images/emoji/bamboo.png
  92. 二进制
      simditor/static/simditor/images/emoji/banana.png
  93. 二进制
      simditor/static/simditor/images/emoji/bangbang.png
  94. 二进制
      simditor/static/simditor/images/emoji/bank.png
  95. 二进制
      simditor/static/simditor/images/emoji/bar_chart.png
  96. 二进制
      simditor/static/simditor/images/emoji/barber.png
  97. 二进制
      simditor/static/simditor/images/emoji/baseball.png
  98. 二进制
      simditor/static/simditor/images/emoji/basketball.png
  99. 二进制
      simditor/static/simditor/images/emoji/bath.png
  100. 二进制
      simditor/static/simditor/images/emoji/bathtub.png
  101. 二进制
      simditor/static/simditor/images/emoji/battery.png
  102. 二进制
      simditor/static/simditor/images/emoji/bear.png
  103. 二进制
      simditor/static/simditor/images/emoji/beer.png
  104. 二进制
      simditor/static/simditor/images/emoji/beers.png
  105. 二进制
      simditor/static/simditor/images/emoji/beetle.png
  106. 二进制
      simditor/static/simditor/images/emoji/beginner.png
  107. 二进制
      simditor/static/simditor/images/emoji/bell.png
  108. 二进制
      simditor/static/simditor/images/emoji/bento.png
  109. 二进制
      simditor/static/simditor/images/emoji/bicyclist.png
  110. 二进制
      simditor/static/simditor/images/emoji/bike.png
  111. 二进制
      simditor/static/simditor/images/emoji/bikini.png
  112. 二进制
      simditor/static/simditor/images/emoji/bird.png
  113. 二进制
      simditor/static/simditor/images/emoji/birthday.png
  114. 二进制
      simditor/static/simditor/images/emoji/black_circle.png
  115. 二进制
      simditor/static/simditor/images/emoji/black_joker.png
  116. 二进制
      simditor/static/simditor/images/emoji/black_nib.png
  117. 二进制
      simditor/static/simditor/images/emoji/black_square.png
  118. 二进制
      simditor/static/simditor/images/emoji/black_square_button.png
  119. 二进制
      simditor/static/simditor/images/emoji/blossom.png
  120. 二进制
      simditor/static/simditor/images/emoji/blowfish.png
  121. 二进制
      simditor/static/simditor/images/emoji/blue_book.png
  122. 二进制
      simditor/static/simditor/images/emoji/blue_car.png
  123. 二进制
      simditor/static/simditor/images/emoji/blue_heart.png
  124. 二进制
      simditor/static/simditor/images/emoji/blush.png
  125. 二进制
      simditor/static/simditor/images/emoji/boar.png
  126. 二进制
      simditor/static/simditor/images/emoji/boat.png
  127. 二进制
      simditor/static/simditor/images/emoji/bomb.png
  128. 二进制
      simditor/static/simditor/images/emoji/book.png
  129. 二进制
      simditor/static/simditor/images/emoji/bookmark.png
  130. 二进制
      simditor/static/simditor/images/emoji/bookmark_tabs.png
  131. 二进制
      simditor/static/simditor/images/emoji/books.png
  132. 二进制
      simditor/static/simditor/images/emoji/boom.png
  133. 二进制
      simditor/static/simditor/images/emoji/boot.png
  134. 二进制
      simditor/static/simditor/images/emoji/bouquet.png
  135. 二进制
      simditor/static/simditor/images/emoji/bow.png
  136. 二进制
      simditor/static/simditor/images/emoji/bowling.png
  137. 二进制
      simditor/static/simditor/images/emoji/bowtie.png
  138. 二进制
      simditor/static/simditor/images/emoji/boy.png
  139. 二进制
      simditor/static/simditor/images/emoji/bread.png
  140. 二进制
      simditor/static/simditor/images/emoji/bride_with_veil.png
  141. 二进制
      simditor/static/simditor/images/emoji/bridge_at_night.png
  142. 二进制
      simditor/static/simditor/images/emoji/briefcase.png
  143. 二进制
      simditor/static/simditor/images/emoji/broken_heart.png
  144. 二进制
      simditor/static/simditor/images/emoji/bug.png
  145. 二进制
      simditor/static/simditor/images/emoji/bulb.png
  146. 二进制
      simditor/static/simditor/images/emoji/bullettrain_front.png
  147. 二进制
      simditor/static/simditor/images/emoji/bullettrain_side.png
  148. 二进制
      simditor/static/simditor/images/emoji/bus.png
  149. 二进制
      simditor/static/simditor/images/emoji/busstop.png
  150. 二进制
      simditor/static/simditor/images/emoji/bust_in_silhouette.png
  151. 二进制
      simditor/static/simditor/images/emoji/busts_in_silhouette.png
  152. 二进制
      simditor/static/simditor/images/emoji/cactus.png
  153. 二进制
      simditor/static/simditor/images/emoji/cake.png
  154. 二进制
      simditor/static/simditor/images/emoji/calendar.png
  155. 二进制
      simditor/static/simditor/images/emoji/calling.png
  156. 二进制
      simditor/static/simditor/images/emoji/camel.png
  157. 二进制
      simditor/static/simditor/images/emoji/camera.png
  158. 二进制
      simditor/static/simditor/images/emoji/cancer.png
  159. 二进制
      simditor/static/simditor/images/emoji/candy.png
  160. 二进制
      simditor/static/simditor/images/emoji/capital_abcd.png
  161. 二进制
      simditor/static/simditor/images/emoji/capricorn.png
  162. 二进制
      simditor/static/simditor/images/emoji/car.png
  163. 二进制
      simditor/static/simditor/images/emoji/card_index.png
  164. 二进制
      simditor/static/simditor/images/emoji/carousel_horse.png
  165. 二进制
      simditor/static/simditor/images/emoji/cat.png
  166. 二进制
      simditor/static/simditor/images/emoji/cat2.png
  167. 二进制
      simditor/static/simditor/images/emoji/cd.png
  168. 二进制
      simditor/static/simditor/images/emoji/chart.png
  169. 二进制
      simditor/static/simditor/images/emoji/chart_with_downwards_trend.png
  170. 二进制
      simditor/static/simditor/images/emoji/chart_with_upwards_trend.png
  171. 二进制
      simditor/static/simditor/images/emoji/checkered_flag.png
  172. 二进制
      simditor/static/simditor/images/emoji/cherries.png
  173. 二进制
      simditor/static/simditor/images/emoji/cherry_blossom.png
  174. 二进制
      simditor/static/simditor/images/emoji/chestnut.png
  175. 二进制
      simditor/static/simditor/images/emoji/chicken.png
  176. 二进制
      simditor/static/simditor/images/emoji/children_crossing.png
  177. 二进制
      simditor/static/simditor/images/emoji/chocolate_bar.png
  178. 二进制
      simditor/static/simditor/images/emoji/christmas_tree.png
  179. 二进制
      simditor/static/simditor/images/emoji/church.png
  180. 二进制
      simditor/static/simditor/images/emoji/cinema.png
  181. 二进制
      simditor/static/simditor/images/emoji/circus_tent.png
  182. 二进制
      simditor/static/simditor/images/emoji/city_sunrise.png
  183. 二进制
      simditor/static/simditor/images/emoji/city_sunset.png
  184. 二进制
      simditor/static/simditor/images/emoji/cl.png
  185. 二进制
      simditor/static/simditor/images/emoji/clap.png
  186. 二进制
      simditor/static/simditor/images/emoji/clapper.png
  187. 二进制
      simditor/static/simditor/images/emoji/clipboard.png
  188. 二进制
      simditor/static/simditor/images/emoji/clock1.png
  189. 二进制
      simditor/static/simditor/images/emoji/clock10.png
  190. 二进制
      simditor/static/simditor/images/emoji/clock1030.png
  191. 二进制
      simditor/static/simditor/images/emoji/clock11.png
  192. 二进制
      simditor/static/simditor/images/emoji/clock1130.png
  193. 二进制
      simditor/static/simditor/images/emoji/clock12.png
  194. 二进制
      simditor/static/simditor/images/emoji/clock1230.png
  195. 二进制
      simditor/static/simditor/images/emoji/clock130.png
  196. 二进制
      simditor/static/simditor/images/emoji/clock2.png
  197. 二进制
      simditor/static/simditor/images/emoji/clock230.png
  198. 二进制
      simditor/static/simditor/images/emoji/clock3.png
  199. 二进制
      simditor/static/simditor/images/emoji/clock330.png
  200. 二进制
      simditor/static/simditor/images/emoji/clock4.png
  201. 二进制
      simditor/static/simditor/images/emoji/clock430.png
  202. 二进制
      simditor/static/simditor/images/emoji/clock5.png
  203. 二进制
      simditor/static/simditor/images/emoji/clock530.png
  204. 二进制
      simditor/static/simditor/images/emoji/clock6.png
  205. 二进制
      simditor/static/simditor/images/emoji/clock630.png
  206. 二进制
      simditor/static/simditor/images/emoji/clock7.png
  207. 二进制
      simditor/static/simditor/images/emoji/clock730.png
  208. 二进制
      simditor/static/simditor/images/emoji/clock8.png
  209. 二进制
      simditor/static/simditor/images/emoji/clock830.png
  210. 二进制
      simditor/static/simditor/images/emoji/clock9.png
  211. 二进制
      simditor/static/simditor/images/emoji/clock930.png
  212. 二进制
      simditor/static/simditor/images/emoji/closed_book.png
  213. 二进制
      simditor/static/simditor/images/emoji/closed_lock_with_key.png
  214. 二进制
      simditor/static/simditor/images/emoji/closed_umbrella.png
  215. 二进制
      simditor/static/simditor/images/emoji/cloud.png
  216. 二进制
      simditor/static/simditor/images/emoji/clubs.png
  217. 二进制
      simditor/static/simditor/images/emoji/cn.png
  218. 二进制
      simditor/static/simditor/images/emoji/cocktail.png
  219. 二进制
      simditor/static/simditor/images/emoji/coffee.png
  220. 二进制
      simditor/static/simditor/images/emoji/cold_sweat.png
  221. 二进制
      simditor/static/simditor/images/emoji/collision.png
  222. 二进制
      simditor/static/simditor/images/emoji/computer.png
  223. 二进制
      simditor/static/simditor/images/emoji/confetti_ball.png
  224. 二进制
      simditor/static/simditor/images/emoji/confounded.png
  225. 二进制
      simditor/static/simditor/images/emoji/confused.png
  226. 二进制
      simditor/static/simditor/images/emoji/congratulations.png
  227. 二进制
      simditor/static/simditor/images/emoji/construction.png
  228. 二进制
      simditor/static/simditor/images/emoji/construction_worker.png
  229. 二进制
      simditor/static/simditor/images/emoji/convenience_store.png
  230. 二进制
      simditor/static/simditor/images/emoji/cookie.png
  231. 二进制
      simditor/static/simditor/images/emoji/cool.png
  232. 二进制
      simditor/static/simditor/images/emoji/cop.png
  233. 二进制
      simditor/static/simditor/images/emoji/copyright.png
  234. 二进制
      simditor/static/simditor/images/emoji/corn.png
  235. 二进制
      simditor/static/simditor/images/emoji/couple.png
  236. 二进制
      simditor/static/simditor/images/emoji/couple_with_heart.png
  237. 二进制
      simditor/static/simditor/images/emoji/couplekiss.png
  238. 二进制
      simditor/static/simditor/images/emoji/cow.png
  239. 二进制
      simditor/static/simditor/images/emoji/cow2.png
  240. 二进制
      simditor/static/simditor/images/emoji/credit_card.png
  241. 二进制
      simditor/static/simditor/images/emoji/crocodile.png
  242. 二进制
      simditor/static/simditor/images/emoji/crossed_flags.png
  243. 二进制
      simditor/static/simditor/images/emoji/crown.png
  244. 二进制
      simditor/static/simditor/images/emoji/cry.png
  245. 二进制
      simditor/static/simditor/images/emoji/crying_cat_face.png
  246. 二进制
      simditor/static/simditor/images/emoji/crystal_ball.png
  247. 二进制
      simditor/static/simditor/images/emoji/cupid.png
  248. 二进制
      simditor/static/simditor/images/emoji/curly_loop.png
  249. 二进制
      simditor/static/simditor/images/emoji/currency_exchange.png
  250. 二进制
      simditor/static/simditor/images/emoji/curry.png
  251. 二进制
      simditor/static/simditor/images/emoji/custard.png
  252. 二进制
      simditor/static/simditor/images/emoji/customs.png
  253. 二进制
      simditor/static/simditor/images/emoji/cyclone.png
  254. 二进制
      simditor/static/simditor/images/emoji/dancer.png
  255. 二进制
      simditor/static/simditor/images/emoji/dancers.png
  256. 二进制
      simditor/static/simditor/images/emoji/dango.png
  257. 二进制
      simditor/static/simditor/images/emoji/dart.png
  258. 二进制
      simditor/static/simditor/images/emoji/dash.png
  259. 二进制
      simditor/static/simditor/images/emoji/date.png
  260. 二进制
      simditor/static/simditor/images/emoji/de.png
  261. 二进制
      simditor/static/simditor/images/emoji/deciduous_tree.png
  262. 二进制
      simditor/static/simditor/images/emoji/department_store.png
  263. 二进制
      simditor/static/simditor/images/emoji/diamond_shape_with_a_dot_inside.png
  264. 二进制
      simditor/static/simditor/images/emoji/diamonds.png
  265. 二进制
      simditor/static/simditor/images/emoji/disappointed.png
  266. 二进制
      simditor/static/simditor/images/emoji/dizzy.png
  267. 二进制
      simditor/static/simditor/images/emoji/dizzy_face.png
  268. 二进制
      simditor/static/simditor/images/emoji/do_not_litter.png
  269. 二进制
      simditor/static/simditor/images/emoji/dog.png
  270. 二进制
      simditor/static/simditor/images/emoji/dog2.png
  271. 二进制
      simditor/static/simditor/images/emoji/dollar.png
  272. 二进制
      simditor/static/simditor/images/emoji/dolls.png
  273. 二进制
      simditor/static/simditor/images/emoji/dolphin.png
  274. 二进制
      simditor/static/simditor/images/emoji/door.png
  275. 二进制
      simditor/static/simditor/images/emoji/doughnut.png
  276. 二进制
      simditor/static/simditor/images/emoji/dragon.png
  277. 二进制
      simditor/static/simditor/images/emoji/dragon_face.png
  278. 二进制
      simditor/static/simditor/images/emoji/dress.png
  279. 二进制
      simditor/static/simditor/images/emoji/dromedary_camel.png
  280. 二进制
      simditor/static/simditor/images/emoji/droplet.png
  281. 二进制
      simditor/static/simditor/images/emoji/dvd.png
  282. 二进制
      simditor/static/simditor/images/emoji/e-mail.png
  283. 二进制
      simditor/static/simditor/images/emoji/ear.png
  284. 二进制
      simditor/static/simditor/images/emoji/ear_of_rice.png
  285. 二进制
      simditor/static/simditor/images/emoji/earth_africa.png
  286. 二进制
      simditor/static/simditor/images/emoji/earth_americas.png
  287. 二进制
      simditor/static/simditor/images/emoji/earth_asia.png
  288. 二进制
      simditor/static/simditor/images/emoji/egg.png
  289. 二进制
      simditor/static/simditor/images/emoji/eggplant.png
  290. 二进制
      simditor/static/simditor/images/emoji/eight.png
  291. 二进制
      simditor/static/simditor/images/emoji/eight_pointed_black_star.png
  292. 二进制
      simditor/static/simditor/images/emoji/eight_spoked_asterisk.png
  293. 二进制
      simditor/static/simditor/images/emoji/eightball.png
  294. 二进制
      simditor/static/simditor/images/emoji/electric_plug.png
  295. 二进制
      simditor/static/simditor/images/emoji/elephant.png
  296. 二进制
      simditor/static/simditor/images/emoji/email.png
  297. 0 0
      simditor/static/simditor/images/emoji/emojify.css

+ 1 - 1
account/admin.py

@@ -8,7 +8,7 @@ from mch.models import ConsumeInfoSubmitLogInfo
8 8
 
9 9
 
10 10
 class UserInfoAdmin(ChangeOnlyModelAdmin, admin.ModelAdmin):
11
-    list_display = ('user_id', 'nickname', 'phone', 'unionid', 'openid', 'openid_miniapp', 'location', 'balance', 'user_status', 'test_user', 'status', 'created_at', 'updated_at')
11
+    list_display = ('user_id', 'nickname', 'phone', 'unionid', 'openid', 'openid_miniapp', 'location', 'balance', 'integral', 'freeze_integral', 'user_status', 'test_user', 'status', 'created_at', 'updated_at')
12 12
     list_filter = ('test_user', 'sex', 'user_status', 'status')
13 13
     readonly_fields = ('user_id', )
14 14
     search_fields = ('user_id', 'username', 'unionid', 'openid', 'openid_miniapp', 'name', 'phone', 'location')

+ 35 - 0
account/migrations/0037_auto_20191119_1348.py

@@ -0,0 +1,35 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2019-11-19 05:48
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('account', '0036_auto_20190826_1537'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='userinfo',
17
+            name='freeze_integral',
18
+            field=models.IntegerField(default=0, help_text='\u4f1a\u5458\u51bb\u7ed3\u79ef\u5206', verbose_name='freeze_integral'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='userinfo',
22
+            name='integral',
23
+            field=models.IntegerField(default=0, help_text='\u4f1a\u5458\u79ef\u5206', verbose_name='integral'),
24
+        ),
25
+        migrations.AddField(
26
+            model_name='userinfo',
27
+            name='level',
28
+            field=models.IntegerField(choices=[(0, '\u975e\u4f1a\u5458'), (1, 'LRC\u4f1a\u5458'), (2, '\u94f6\u5361\u4f1a\u5458'), (3, '\u91d1\u5361\u4f1a\u5458'), (4, '\u767d\u91d1\u5361\u4f1a\u5458'), (5, '\u9ed1\u91d1\u5361\u4f1a\u5458')], default=0, help_text='\u4f1a\u5458\u7b49\u7ea7', verbose_name='level'),
29
+        ),
30
+        migrations.AddField(
31
+            model_name='userinfo',
32
+            name='shots_num',
33
+            field=models.IntegerField(default=0, help_text='\u4e3b\u6301\u955c\u5934\u6570', verbose_name='shots_num'),
34
+        ),
35
+    ]

+ 27 - 0
account/models.py

@@ -290,6 +290,22 @@ class UserInfo(BaseModelMixin, LensmanTypeBoolMixin):
290 290
         (FEMALE, u'女'),
291 291
     )
292 292
 
293
+    MEMBER_NO = 0
294
+    MEMBER_LRC = 1
295
+    MEMBER_SILVER = 2
296
+    MEMBER_GOLD = 3
297
+    MEMBER_WHITE_GOLD = 4
298
+    MEMBER_BLACK_GOLD = 5
299
+
300
+    LEVEL_TUPLE = (
301
+        (MEMBER_NO, u'非会员'),
302
+        (MEMBER_LRC, u'LRC会员'),
303
+        (MEMBER_SILVER, u'银卡会员'),
304
+        (MEMBER_GOLD, u'金卡会员'),
305
+        (MEMBER_WHITE_GOLD, u'白金卡会员'),
306
+        (MEMBER_BLACK_GOLD, u'黑金卡会员'),
307
+    )
308
+
293 309
     user_id = models.CharField(_(u'user_id'), max_length=32, blank=True, null=True, help_text=u'用户唯一标识', db_index=True, unique=True)
294 310
 
295 311
     user_from = models.IntegerField(_(u'user_from'), choices=USER_FROM, default=APP_USER, help_text=u'用户来源')
@@ -341,6 +357,13 @@ class UserInfo(BaseModelMixin, LensmanTypeBoolMixin):
341 357
 
342 358
     test_user = models.BooleanField(_(u'test_user'), default=False, help_text=_(u'是否为测试用户'), db_index=True)
343 359
 
360
+    # 会员信息
361
+    integral = models.IntegerField(_(u'integral'), default=0, help_text=u'会员积分')
362
+    freeze_integral = models.IntegerField(_(u'freeze_integral'), default=0, help_text=u'会员冻结积分')
363
+
364
+    shots_num = models.IntegerField(_(u'shots_num'), default=0, help_text=u'主持镜头数')
365
+    level = models.IntegerField(_(u'level'), choices=LEVEL_TUPLE, default=MEMBER_NO, help_text=u'会员等级')
366
+
344 367
     class Meta:
345 368
         verbose_name = _(u'userinfo')
346 369
         verbose_name_plural = _(u'userinfo')
@@ -349,6 +372,10 @@ class UserInfo(BaseModelMixin, LensmanTypeBoolMixin):
349 372
         return unicode(self.pk)
350 373
 
351 374
     @property
375
+    def final_integral(self):
376
+        return self.integral + self.freeze_integral
377
+
378
+    @property
352 379
     def final_nickname(self):
353 380
         if self.user_from == self.APP_USER:
354 381
             return self.username

+ 200 - 0
api/member_views.py

@@ -0,0 +1,200 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from __future__ import division
4
+
5
+from django.conf import settings
6
+from django_logit import logit
7
+from django_response import response
8
+from paginator import pagination
9
+
10
+from account.models import UserInfo
11
+from member.models import GoodsInfo, MemberActivityInfo, RightInfo
12
+from utils.error.errno_utils import UserStatusCode
13
+from utils.redis.rshot import get_shot_member_data
14
+
15
+
16
+@logit
17
+def member(request):
18
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
19
+    user_id = request.POST.get('user_id', '')
20
+
21
+    # 校验用户是否存在
22
+    try:
23
+        user = UserInfo.objects.get(user_id=user_id)
24
+    except UserInfo.DoesNotExist:
25
+        return response(UserStatusCode.USER_NOT_FOUND)
26
+
27
+    rights = RightInfo.objects.filter(status=True).order_by('position')
28
+    rights = [right.data for right in rights]
29
+
30
+    goods = GoodsInfo.objects.filter(status=True).order_by('position')
31
+    goods = [good.data for good in goods]
32
+
33
+    return response(200, data={
34
+        'nickname': user.final_nickname,
35
+        'avatar': user.avatar,
36
+        'integral': user.integral,
37
+        'freeze_integral': user.freeze_integral,
38
+        'final_integral': user.final_integral,
39
+        'shots_num': user.shots_num,
40
+        'level': user.level,
41
+        'rights': rights,
42
+        'goods': goods,
43
+    })
44
+
45
+
46
+@logit
47
+def rights(request):
48
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
49
+    user_id = request.POST.get('user_id', '')
50
+    level = request.POST.get('level', '')
51
+
52
+    # 校验用户是否存在
53
+    try:
54
+        user = UserInfo.objects.get(user_id=user_id)
55
+    except UserInfo.DoesNotExist:
56
+        return response(UserStatusCode.USER_NOT_FOUND)
57
+
58
+    rights = RightInfo.objects.filter(status=True).order_by('position')
59
+    rights = [right.data for right in rights]
60
+
61
+    return response(200, data={
62
+        'nickname': user.final_nickname,
63
+        'avatar': user.avatar,
64
+        'integral': user.integral,
65
+        'freeze_integral': user.freeze_integral,
66
+        'final_integral': user.final_integral,
67
+        'shots_num': user.shots_num,
68
+        'level': user.level,
69
+        'rights': rights,
70
+    })
71
+
72
+
73
+@logit
74
+def right_detail(request):
75
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
76
+    user_id = request.POST.get('user_id', '')
77
+    right_id = request.POST.get('right_id', '')
78
+
79
+    try:
80
+        right = RightInfo.objects.get(right_id=right_id)
81
+    except RightInfo.DoesNotExist:
82
+        return response()
83
+
84
+    return response(200, data={
85
+        'right': right.data,
86
+    })
87
+
88
+
89
+@logit
90
+def goods(request):
91
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
92
+    user_id = request.POST.get('user_id', '')
93
+
94
+    # 校验用户是否存在
95
+    try:
96
+        user = UserInfo.objects.get(user_id=user_id)
97
+    except UserInfo.DoesNotExist:
98
+        return response(UserStatusCode.USER_NOT_FOUND)
99
+
100
+    raw_goods = GoodsInfo.objects.filter(status=True).order_by('position')
101
+    banners = goods = []
102
+    for good in raw_goods:
103
+        if good.is_slider:
104
+            banners.append(good.data)
105
+        else:
106
+            goods.append(good.data)
107
+
108
+    return response(200, data={
109
+        'nickname': user.final_nickname,
110
+        'avatar': user.avatar,
111
+        'integral': user.integral,
112
+        'freeze_integral': user.freeze_integral,
113
+        'final_integral': user.final_integral,
114
+        'shots_num': user.shots_num,
115
+        'level': user.level,
116
+        'banners': banners,
117
+        'goods': goods,
118
+    })
119
+
120
+
121
+@logit
122
+def good_detail(request):
123
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
124
+    user_id = request.POST.get('user_id', '')
125
+    good_id = request.POST.get('good_id', '')
126
+
127
+    # 校验用户是否存在
128
+    try:
129
+        user = UserInfo.objects.get(user_id=user_id)
130
+    except UserInfo.DoesNotExist:
131
+        return response(UserStatusCode.USER_NOT_FOUND)
132
+
133
+    try:
134
+        good = GoodsInfo.objects.get(good_id=good_id)
135
+    except GoodsInfo.DoesNotExist:
136
+        return response()
137
+
138
+    return response(200, data={
139
+        'nickname': user.final_nickname,
140
+        'avatar': user.avatar,
141
+        'integral': user.integral,
142
+        'freeze_integral': user.freeze_integral,
143
+        'final_integral': user.final_integral,
144
+        'shots_num': user.shots_num,
145
+        'level': user.level,
146
+        'good': good.data,
147
+    })
148
+
149
+
150
+@logit
151
+def integrals(request):
152
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
153
+
154
+    return response(200, data={
155
+        'shots_types': get_shot_member_data(),
156
+        'enable_photo_upvote_integral': True,
157
+        'photo_upvote_integrals': {
158
+            'headers': ['排名', '日', '周', '月'],
159
+            'ranks': [
160
+                ['第1名', '10', '20', '30'],
161
+                ['第2名', '5', '10', '15'],
162
+                ['第3名', '3', '6', '9'],
163
+                ['第4-10名', '1', '2', '3'],
164
+            ]
165
+        },
166
+        'enable_activity_integral': True,
167
+    })
168
+
169
+
170
+@logit
171
+def activity_list(request):
172
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
173
+
174
+    raw_activitys = MemberActivityInfo.objects.filter(status=True).order_by('position')
175
+    banners = activitys = []
176
+    for act in raw_activitys:
177
+        if act.is_slider:
178
+            banners.append(act.data)
179
+        else:
180
+            goods.append(act.data)
181
+
182
+    return response(200, data={
183
+        'banners': banners,
184
+        'activitys': activitys,
185
+    })
186
+
187
+
188
+@logit
189
+def activity_detail(request):
190
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
191
+    activity_id = request.POST.get('activity_id')
192
+
193
+    try:
194
+        act = MemberActivityInfo.objects.get(pk=activity_id, status=True)
195
+    except MemberActivityInfo.DoesNotExist:
196
+        return response()
197
+
198
+    return response(200, data={
199
+        'activity': act.data,
200
+    })

+ 21 - 2
api/urls.py

@@ -5,8 +5,8 @@ from django_file_upload import views as file_views
5 5
 
6 6
 from account import tourguide_views
7 7
 from account import views as account_views
8
-from api import (admin_views, clerk_views, distributor_views, encrypt_views, mch_views, model_views, operator_views,
9
-                 sr_views)
8
+from api import (admin_views, clerk_views, distributor_views, encrypt_views, mch_views, member_views, model_views,
9
+                 operator_views, sr_views)
10 10
 from box import views as box_views
11 11
 from geo import views as geo_views
12 12
 from group import (groupuser_views, lensman_views, tourguidegroup_views, tourguidegroupadmin_views,
@@ -298,3 +298,22 @@ urlpatterns += [
298 298
 urlpatterns += [
299 299
     url(r'^supersr/sr/tj$', sales_views.supersr_sr_tj, name='supersr_sr_tj'),
300 300
 ]
301
+
302
+urlpatterns += [
303
+    url(r'^member$', member_views.member, name='member'),
304
+
305
+    url(r'^member/rights$', member_views.rights, name='member_rights'),
306
+    url(r'^member/right/detail$', member_views.right_detail, name='member_right_detail'),
307
+    url(r'^member/goods$', member_views.goods, name='member_goods'),
308
+    url(r'^member/good/detail$', member_views.good_detail, name='member_good_detail'),
309
+    url(r'^member/integrals$', member_views.integrals, name='member_integrals'),
310
+
311
+    url(r'^member/activity/list$', member_views.activity_list, name='member_activity_list'),
312
+    url(r'^member/activity/detail$', member_views.activity_detail, name='member_activity_detail'),
313
+
314
+    url(r'^rights$', member_views.rights, name='rights'),
315
+    url(r'^right/detail$', member_views.right_detail, name='right_detail'),
316
+    url(r'^goods$', member_views.goods, name='goods'),
317
+    url(r'^good/detail$', member_views.good_detail, name='good_detail'),
318
+    url(r'^integrals$', member_views.integrals, name='integrals'),
319
+]

+ 25 - 0
integral/migrations/0011_auto_20191119_1348.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2019-11-19 05:48
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('integral', '0010_auto_20190909_1456'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='saleclerksubmitloginfo',
17
+            name='has_scan',
18
+            field=models.BooleanField(db_index=True, default=False, help_text='\u662f\u5426\u88ab\u6d88\u8d39\u8005\u626b\u8fc7', verbose_name='has_scan'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='saleclerksubmitloginfo',
22
+            name='trackingNo',
23
+            field=models.CharField(blank=True, help_text='\u5feb\u9012\u5355\u53f7', max_length=32, null=True, verbose_name='trackingNo'),
24
+        ),
25
+    ]

+ 21 - 0
kodo/settings.py

@@ -49,6 +49,7 @@ INSTALLED_APPS = (
49 49
     'django_rlog',
50 50
     'django_we',
51 51
     'corsheaders',
52
+    'simditor',
52 53
     'api',
53 54
     'account',
54 55
     'box',
@@ -58,6 +59,7 @@ INSTALLED_APPS = (
58 59
     'integral',
59 60
     'logs',
60 61
     'mch',
62
+    'member',
61 63
     'message',
62 64
     'miniapp',
63 65
     'operation',
@@ -346,6 +348,25 @@ CORS_ORIGIN_WHITELIST = (
346 348
 )
347 349
 CORS_ALLOW_CREDENTIALS = True
348 350
 
351
+# 富文本编辑器
352
+SIMDITOR_UPLOAD_PATH = 'uploads/'
353
+SIMDITOR_TOOLBAR = [
354
+    'markdown', 'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale',
355
+    'color', '|', 'ol', 'ul', 'blockquote', 'code', 'table', '|', 'link',
356
+    'image', 'hr', '|', 'indent', 'outdent', 'alignment', 'checklist', 'fullscreen', 'emoji'
357
+]
358
+SIMDITOR_CONFIGS = {
359
+    'toolbar': SIMDITOR_TOOLBAR,
360
+    'upload': {
361
+        'url': '/simditor/upload/',
362
+        'fileKey': 'upload',
363
+        'image_size': 1024 * 1024 * 4  # max image size 4MB
364
+    },
365
+    'emoji': {
366
+        'imagePath': '/static/simditor/images/emoji/'
367
+    }
368
+}
369
+
349 370
 # 错误信息邮件设置
350 371
 # Email address that error messages come from.
351 372
 SERVER_EMAIL = 'kimi@pai.ai'

+ 8 - 1
mch/admin.py

@@ -10,6 +10,7 @@ from pysnippets.strsnippets import strip
10 10
 
11 11
 from mch.models import (ActivityInfo, AdministratorInfo, BrandInfo, ConsumeInfoSubmitLogInfo, DistributorInfo,
12 12
                         LatestAppInfo, LatestAppScreenInfo, ModelInfo, OperatorInfo, SaleclerkInfo)
13
+from utils.redis.rshot import update_shot_member_data
13 14
 
14 15
 
15 16
 class AdministratorInfoAdmin(admin.ModelAdmin):
@@ -64,7 +65,7 @@ class BrandInfoAdmin(admin.ModelAdmin):
64 65
 
65 66
 class ModelInfoAdmin(admin.ModelAdmin):
66 67
     list_display = ('brand_id', 'brand_name', 'jancode', 'model_id', 'model_name', 'model_uni_name', 'model_full_name', 'model_descr', 'category', 'warehouse', 'image', 'url', 'image2', 'factory_yuan', 'integral', 'position', 'display', 'status', 'created_at', 'updated_at')
67
-    list_filter = ('brand_name', 'category', 'warehouse', 'display', 'status')
68
+    list_filter = ('brand_name', 'category', 'warehouse', 'shot_type_id', 'display', 'status')
68 69
     readonly_fields = ('brand_name', 'factory_fee')
69 70
     search_fields = ('brand_id', 'brand_name', 'jancode', 'model_id', 'model_name', 'model_uni_name', 'model_full_name', 'model_descr', 'category', 'warehouse')
70 71
 
@@ -80,6 +81,12 @@ class ModelInfoAdmin(admin.ModelAdmin):
80 81
 
81 82
         obj.save()
82 83
 
84
+        update_shot_member_data()
85
+
86
+    def delete_model(self, request, obj):
87
+        obj.delete()
88
+        update_shot_member_data()
89
+
83 90
 
84 91
 class ModelImageInfoAdmin(admin.ModelAdmin):
85 92
     list_display = ('model_id', 'model_name', 'image', 'url', 'position', 'status', 'created_at', 'updated_at')

+ 36 - 0
mch/migrations/0039_auto_20191119_1348.py

@@ -0,0 +1,36 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2019-11-19 05:48
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+import django_models_ext.fileext
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    dependencies = [
12
+        ('mch', '0038_auto_20190826_1625'),
13
+    ]
14
+
15
+    operations = [
16
+        migrations.AddField(
17
+            model_name='modelinfo',
18
+            name='shot_member_image',
19
+            field=models.ImageField(blank=True, help_text='\u955c\u5934\u4f1a\u5458\u56fe\u7247', null=True, upload_to=django_models_ext.fileext.upload_path, verbose_name='shot_member_image'),
20
+        ),
21
+        migrations.AddField(
22
+            model_name='modelinfo',
23
+            name='shot_member_integral',
24
+            field=models.IntegerField(default=0, help_text='\u955c\u5934\u4f1a\u5458\u79ef\u5206', verbose_name='shot_member_integral'),
25
+        ),
26
+        migrations.AddField(
27
+            model_name='modelinfo',
28
+            name='shot_type_id',
29
+            field=models.CharField(blank=True, db_index=True, help_text='\u955c\u5934\u7c7b\u578b\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='shot_type_id'),
30
+        ),
31
+        migrations.AddField(
32
+            model_name='saleclerkinfo',
33
+            name='is_online_sales',
34
+            field=models.BooleanField(db_index=True, default=False, help_text='\u662f\u5426\u4e3a\u7f51\u9500', verbose_name='is_online_sales'),
35
+        ),
36
+    ]

+ 21 - 0
mch/models.py

@@ -161,6 +161,10 @@ class ModelInfo(BaseModelMixin):
161 161
 
162 162
     is_important = models.BooleanField(_(u'is_important'), default=False, help_text=_(u'是否重要型号'), db_index=True)
163 163
 
164
+    shot_type_id = models.CharField(_(u'shot_type_id'), max_length=32, blank=True, null=True, help_text=u'镜头类型唯一标识', db_index=True)
165
+    shot_member_integral = models.IntegerField(_(u'shot_member_integral'), default=0, help_text=u'镜头会员积分')
166
+    shot_member_image = models.ImageField(_(u'shot_member_image'), upload_to=upload_path, blank=True, null=True, help_text=u'镜头会员图片')
167
+
164 168
     class Meta:
165 169
         verbose_name = _(u'型号信息')
166 170
         verbose_name_plural = _(u'型号信息')
@@ -185,6 +189,14 @@ class ModelInfo(BaseModelMixin):
185 189
         return upload_file_url(self.image2)
186 190
 
187 191
     @property
192
+    def shot_member_image_path(self):
193
+        return upload_file_path(self.shot_member_image)
194
+
195
+    @property
196
+    def shot_member_image_url(self):
197
+        return upload_file_url(self.shot_member_image)
198
+
199
+    @property
188 200
     def data(self):
189 201
         return {
190 202
             'jancode': self.jancode,
@@ -228,6 +240,15 @@ class ModelInfo(BaseModelMixin):
228 240
 
229 241
     fulldata = admindata
230 242
 
243
+    @property
244
+    def shot_member_data(self):
245
+        return {
246
+            'shot_id': self.model_id,
247
+            'shot_name': self.model_name,
248
+            'shot_image': self.shot_member_image_url,
249
+            'integral': self.shot_member_integral,
250
+        }
251
+
231 252
 
232 253
 class ModelImageInfo(BaseModelMixin):
233 254
     model_id = models.CharField(_(u'model_id'), max_length=32, blank=True, null=True, help_text=u'型号唯一标识', db_index=True)

+ 0 - 0
member/__init__.py


+ 38 - 0
member/admin.py

@@ -0,0 +1,38 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.contrib import admin
4
+
5
+from member.models import GoodsInfo, MemberActivityInfo, ShotTypeInfo, RightInfo
6
+from utils.redis.rshot import update_shot_member_data
7
+
8
+
9
+class GoodsInfoAdmin(admin.ModelAdmin):
10
+    list_display = ('good_id', 'good_type', 'title', 'desc', 'image', 'is_slider', 'slider_image', 'integral', 'fee', 'minlevel', 'position', 'status', 'created_at', 'updated_at')
11
+    list_filter = ('is_slider', 'status')
12
+
13
+
14
+class RightInfoAdmin(admin.ModelAdmin):
15
+    list_display = ('right_id', 'icon', 'title', 'subtitle', 'detail', 'minlevel', 'position', 'status', 'created_at', 'updated_at')
16
+
17
+
18
+class ShotTypeInfoAdmin(admin.ModelAdmin):
19
+    list_display = ('shot_type_id', 'shot_type_name', 'position', 'status', 'created_at', 'updated_at')
20
+
21
+    def save_model(self, request, obj, form, change):
22
+        obj.save()
23
+        update_shot_member_data()
24
+
25
+    def delete_model(self, request, obj):
26
+        obj.delete()
27
+        update_shot_member_data()
28
+
29
+
30
+class MemberActivityInfoAdmin(admin.ModelAdmin):
31
+    list_display = ('title', 'subtitle', 'date', 'location', 'image', 'is_slider', 'slider_image', 'position', 'status', 'created_at', 'updated_at')
32
+    list_filter = ('is_slider', 'status')
33
+
34
+
35
+admin.site.register(GoodsInfo, GoodsInfoAdmin)
36
+admin.site.register(RightInfo, RightInfoAdmin)
37
+admin.site.register(ShotTypeInfo, ShotTypeInfoAdmin)
38
+admin.site.register(MemberActivityInfo, MemberActivityInfoAdmin)

+ 8 - 0
member/apps.py

@@ -0,0 +1,8 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.apps import AppConfig
5
+
6
+
7
+class MemberConfig(AppConfig):
8
+    name = 'member'

+ 104 - 0
member/migrations/0001_initial.py

@@ -0,0 +1,104 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2019-11-19 05:48
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+import django_models_ext.fileext
7
+import shortuuidfield.fields
8
+import simditor.fields
9
+
10
+
11
+class Migration(migrations.Migration):
12
+
13
+    initial = True
14
+
15
+    dependencies = [
16
+    ]
17
+
18
+    operations = [
19
+        migrations.CreateModel(
20
+            name='GoodsInfo',
21
+            fields=[
22
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
24
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
25
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
26
+                ('good_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u5546\u54c1\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True)),
27
+                ('good_type', models.IntegerField(choices=[(0, '\u5b9e\u7269'), (1, '\u865a\u62df')], db_index=True, default=1, help_text='\u5546\u54c1\u7c7b\u578b', verbose_name='good_type')),
28
+                ('title', models.CharField(blank=True, help_text='\u5546\u54c1\u540d\u79f0', max_length=255, null=True, verbose_name='title')),
29
+                ('desc', simditor.fields.RichTextField(blank=True, help_text='\u5546\u54c1\u63cf\u8ff0', null=True, verbose_name='desc')),
30
+                ('image', models.ImageField(blank=True, help_text='\u5546\u54c1\u56fe\u7247', null=True, upload_to=django_models_ext.fileext.upload_path, verbose_name='image')),
31
+                ('is_slider', models.BooleanField(db_index=True, default=True, help_text='\u662f\u5426\u4e3a\u8f6e\u64ad\u5546\u54c1', verbose_name='is_slider')),
32
+                ('slider_image', models.ImageField(blank=True, help_text='\u5546\u54c1\u8f6e\u64ad\u56fe\u7247', null=True, upload_to=django_models_ext.fileext.upload_path, verbose_name='slider_image')),
33
+                ('integral', models.IntegerField(default=99999, help_text='\u5151\u6362\u6240\u9700\u79ef\u5206', verbose_name='integral')),
34
+                ('fee', models.IntegerField(default=99999, help_text='\u5151\u6362\u9700\u989d\u5916\u652f\u4ed8\u91d1\u989d\uff0c\u5355\u4f4d\u5206', verbose_name='fee')),
35
+                ('minlevel', models.IntegerField(default=0, help_text='\u5151\u6362\u6700\u4f4e\u4f1a\u5458\u7ea7\u522b', verbose_name='minlevel')),
36
+                ('position', models.IntegerField(db_index=True, default=1, help_text='\u6392\u5e8f', verbose_name='position')),
37
+            ],
38
+            options={
39
+                'verbose_name': '\u5546\u54c1\u4fe1\u606f',
40
+                'verbose_name_plural': '\u5546\u54c1\u4fe1\u606f',
41
+            },
42
+        ),
43
+        migrations.CreateModel(
44
+            name='MemberActivityInfo',
45
+            fields=[
46
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
47
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
48
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
49
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
50
+                ('title', models.CharField(blank=True, help_text='\u6d3b\u52a8\u540d\u79f0', max_length=255, null=True, verbose_name='title')),
51
+                ('subtitle', models.CharField(blank=True, help_text='\u6d3b\u52a8\u4e8c\u7ea7\u540d\u79f0', max_length=255, null=True, verbose_name='subtitle')),
52
+                ('date', models.DateField(blank=True, help_text='\u6d3b\u52a8\u65f6\u95f4', null=True, verbose_name='date')),
53
+                ('location', models.CharField(blank=True, help_text='\u6d3b\u52a8\u5730\u70b9', max_length=255, null=True, verbose_name='location')),
54
+                ('image', models.ImageField(blank=True, help_text='\u6d3b\u52a8\u5185\u5bb9\u56fe\u7247', null=True, upload_to=django_models_ext.fileext.upload_path, verbose_name='image')),
55
+                ('cover', models.ImageField(blank=True, help_text='\u6d3b\u52a8\u5217\u8868\u56fe\u7247', null=True, upload_to=django_models_ext.fileext.upload_path, verbose_name='cover')),
56
+                ('is_slider', models.BooleanField(db_index=True, default=True, help_text='\u662f\u5426\u4e3a\u8f6e\u64ad\u6d3b\u52a8', verbose_name='is_slider')),
57
+                ('slider_image', models.ImageField(blank=True, help_text='\u6d3b\u52a8\u8f6e\u64ad\u56fe\u7247', null=True, upload_to=django_models_ext.fileext.upload_path, verbose_name='slider_image')),
58
+                ('content_rich_text', simditor.fields.RichTextField(blank=True, help_text='\u6d3b\u52a8\u63cf\u8ff0', null=True, verbose_name='content_rich_text')),
59
+                ('share_img_link', models.CharField(blank=True, help_text='\u6d3b\u52a8\u56fe\u7247\u5206\u4eab', max_length=255, null=True, verbose_name='share_img_link')),
60
+                ('share_h5_link', models.CharField(blank=True, help_text='\u6d3b\u52a8H5\u5206\u4eab', max_length=255, null=True, verbose_name='share_h5_link')),
61
+                ('position', models.IntegerField(db_index=True, default=1, help_text='\u6392\u5e8f', verbose_name='position')),
62
+            ],
63
+            options={
64
+                'verbose_name': '\u4f1a\u5458\u6d3b\u52a8\u4fe1\u606f',
65
+                'verbose_name_plural': '\u4f1a\u5458\u6d3b\u52a8\u4fe1\u606f',
66
+            },
67
+        ),
68
+        migrations.CreateModel(
69
+            name='RightInfo',
70
+            fields=[
71
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
72
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
73
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
74
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
75
+                ('right_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u6743\u76ca\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True)),
76
+                ('icon', models.ImageField(blank=True, help_text='\u6743\u76ca\u56fe\u6807', null=True, upload_to=django_models_ext.fileext.upload_path, verbose_name='icon')),
77
+                ('title', models.CharField(blank=True, help_text='\u6743\u76ca\u540d\u79f0', max_length=255, null=True, verbose_name='title')),
78
+                ('subtitle', models.CharField(blank=True, help_text='\u6743\u76ca\u4e8c\u7ea7\u540d\u79f0', max_length=255, null=True, verbose_name='subtitle')),
79
+                ('detail', simditor.fields.RichTextField(blank=True, help_text='\u6743\u76ca\u8be6\u60c5', null=True, verbose_name='detail')),
80
+                ('minlevel', models.IntegerField(default=0, help_text='\u6743\u76ca\u6700\u4f4e\u4f1a\u5458\u7ea7\u522b', verbose_name='minlevel')),
81
+                ('position', models.IntegerField(db_index=True, default=1, help_text='\u6392\u5e8f', verbose_name='position')),
82
+            ],
83
+            options={
84
+                'verbose_name': '\u6743\u76ca\u4fe1\u606f',
85
+                'verbose_name_plural': '\u6743\u76ca\u4fe1\u606f',
86
+            },
87
+        ),
88
+        migrations.CreateModel(
89
+            name='ShotTypeInfo',
90
+            fields=[
91
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
92
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
93
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
94
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
95
+                ('shot_type_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u955c\u5934\u7c7b\u578b\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True)),
96
+                ('shot_type_name', models.CharField(blank=True, help_text='\u955c\u5934\u7c7b\u578b\u540d\u79f0', max_length=255, null=True, verbose_name='shot_type_name')),
97
+                ('position', models.IntegerField(db_index=True, default=1, help_text='\u6392\u5e8f', verbose_name='position')),
98
+            ],
99
+            options={
100
+                'verbose_name': '\u955c\u5934\u7c7b\u578b\u4fe1\u606f',
101
+                'verbose_name_plural': '\u955c\u5934\u7c7b\u578b\u4fe1\u606f',
102
+            },
103
+        ),
104
+    ]

+ 0 - 0
member/migrations/__init__.py


+ 211 - 0
member/models.py

@@ -0,0 +1,211 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import models
4
+from django.utils.translation import ugettext_lazy as _
5
+from django_models_ext import BaseModelMixin, upload_file_path, upload_file_url, upload_path
6
+from simditor.fields import RichTextField
7
+from shortuuidfield import ShortUUIDField
8
+
9
+from mch.models import ModelInfo
10
+
11
+
12
+class GoodsInfo(BaseModelMixin):
13
+    PHYSICAL = 0
14
+    VIRTUAL = 1
15
+
16
+    GOOD_TYPE_TUPLE = (
17
+        (PHYSICAL, u'实物'),
18
+        (VIRTUAL, u'虚拟'),
19
+    )
20
+
21
+    good_id = ShortUUIDField(_(u'good_id'), max_length=32, blank=True, null=True, help_text=u'商品唯一标识', db_index=True, unique=True)
22
+    good_type = models.IntegerField(_(u'good_type'), choices=GOOD_TYPE_TUPLE, default=VIRTUAL, help_text=u'商品类型', db_index=True)
23
+
24
+    title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'商品名称')
25
+    desc = RichTextField(_(u'desc'), blank=True, null=True, help_text=u'商品描述')
26
+
27
+    image = models.ImageField(_(u'image'), upload_to=upload_path, blank=True, null=True, help_text=u'商品图片')
28
+
29
+    is_slider = models.BooleanField(_(u'is_slider'), default=True, help_text=_(u'是否为轮播商品'), db_index=True)
30
+    slider_image = models.ImageField(_(u'slider_image'), upload_to=upload_path, blank=True, null=True, help_text=u'商品轮播图片')
31
+
32
+    integral = models.IntegerField(_(u'integral'), default=99999, help_text=u'兑换所需积分')
33
+    fee = models.IntegerField(_(u'fee'), default=99999, help_text=u'兑换需额外支付金额,单位分')
34
+
35
+    minlevel = models.IntegerField(_(u'minlevel'), default=0, help_text=u'兑换最低会员级别')
36
+
37
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序', db_index=True)
38
+
39
+    class Meta:
40
+        verbose_name = _(u'会员商品信息')
41
+        verbose_name_plural = _(u'会员商品信息')
42
+
43
+    def __unicode__(self):
44
+        return unicode(self.pk)
45
+
46
+    @property
47
+    def image_path(self):
48
+        return upload_file_path(self.image)
49
+
50
+    @property
51
+    def image_url(self):
52
+        return upload_file_url(self.image)
53
+
54
+    @property
55
+    def slider_image_path(self):
56
+        return upload_file_path(self.slider_image)
57
+
58
+    @property
59
+    def slider_image_url(self):
60
+        return upload_file_url(self.slider_image)
61
+
62
+    @property
63
+    def data(self):
64
+        return {
65
+            'good_id': self.good_id,
66
+            'good_type': self.good_type,
67
+            'title': self.title,
68
+            'desc': self.desc,
69
+            'image': self.image_url,
70
+            'slider_image': self.slider_image_url,
71
+            'integral': self.integral,
72
+            'fee': self.fee,
73
+            'minlevel': self.minlevel,
74
+            'able': True,
75
+        }
76
+
77
+
78
+class RightInfo(BaseModelMixin):
79
+    right_id = ShortUUIDField(_(u'right_id'), max_length=32, blank=True, null=True, help_text=u'权益唯一标识', db_index=True, unique=True)
80
+
81
+    icon = models.ImageField(_(u'icon'), upload_to=upload_path, blank=True, null=True, help_text=u'权益图标')
82
+    title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'权益名称')
83
+    subtitle = models.CharField(_(u'subtitle'), max_length=255, blank=True, null=True, help_text=u'权益二级名称')
84
+    detail = RichTextField(_(u'detail'), blank=True, null=True, help_text=u'权益详情')
85
+
86
+    minlevel = models.IntegerField(_(u'minlevel'), default=0, help_text=u'权益最低会员级别')
87
+
88
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序', db_index=True)
89
+
90
+    class Meta:
91
+        verbose_name = _(u'会员权益信息')
92
+        verbose_name_plural = _(u'会员权益信息')
93
+
94
+    def __unicode__(self):
95
+        return unicode(self.pk)
96
+
97
+    @property
98
+    def icon_path(self):
99
+        return upload_file_path(self.icon)
100
+
101
+    @property
102
+    def icon_url(self):
103
+        return upload_file_url(self.icon)
104
+
105
+    @property
106
+    def data(self):
107
+        return {
108
+            'right_id': self.right_id,
109
+            'icon': self.icon_url,
110
+            'title': self.title,
111
+            'subtitle': self.subtitle,
112
+            'detail': self.detail,
113
+            'minlevel': self.minlevel,
114
+            "able": True,
115
+            "left_num": 3,
116
+            "left_tip": 3,
117
+        }
118
+
119
+
120
+class ShotTypeInfo(BaseModelMixin):
121
+    shot_type_id = ShortUUIDField(_(u'shot_type_id'), max_length=32, blank=True, null=True, help_text=u'镜头类型唯一标识', db_index=True, unique=True)
122
+    shot_type_name = models.CharField(_(u'shot_type_name'), max_length=255, blank=True, null=True, help_text=u'镜头类型名称')
123
+
124
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序', db_index=True)
125
+
126
+    class Meta:
127
+        verbose_name = _(u'镜头类型信息')
128
+        verbose_name_plural = _(u'镜头类型信息')
129
+
130
+    def __unicode__(self):
131
+        return unicode(self.pk)
132
+
133
+    @property
134
+    def shots(self):
135
+        models = ModelInfo.objects.filter(shot_type_id=self.shot_type_id, status=True)
136
+        return [model.shot_data for model in models]
137
+
138
+    @property
139
+    def data(self):
140
+        return {
141
+            'type_id': self.shot_type_id,
142
+            'type_name': self.shot_type_name,
143
+            'shots': self.shots,
144
+        }
145
+
146
+
147
+class MemberActivityInfo(BaseModelMixin):
148
+    title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'活动名称')
149
+    subtitle = models.CharField(_(u'subtitle'), max_length=255, blank=True, null=True, help_text=u'活动二级名称')
150
+
151
+    date = models.DateField(_(u'date'), blank=True, null=True, help_text=u'活动时间')
152
+    location = models.CharField(_(u'location'), max_length=255, blank=True, null=True, help_text=u'活动地点')
153
+
154
+    image = models.ImageField(_(u'image'), upload_to=upload_path, blank=True, null=True, help_text=u'活动内容图片')
155
+
156
+    cover = models.ImageField(_(u'cover'), upload_to=upload_path, blank=True, null=True, help_text=u'活动列表图片')
157
+
158
+    is_slider = models.BooleanField(_(u'is_slider'), default=True, help_text=_(u'是否为轮播活动'), db_index=True)
159
+    slider_image = models.ImageField(_(u'slider_image'), upload_to=upload_path, blank=True, null=True, help_text=u'活动轮播图片')
160
+
161
+    content_rich_text = RichTextField(_(u'content_rich_text'), blank=True, null=True, help_text=u'活动描述')
162
+
163
+    share_img_link = models.CharField(_(u'share_img_link'), max_length=255, blank=True, null=True, help_text=u'活动图片分享')
164
+    share_h5_link = models.CharField(_(u'share_h5_link'), max_length=255, blank=True, null=True, help_text=u'活动H5分享')
165
+
166
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序', db_index=True)
167
+
168
+    class Meta:
169
+        verbose_name = _(u'会员活动信息')
170
+        verbose_name_plural = _(u'会员活动信息')
171
+
172
+    def __unicode__(self):
173
+        return unicode(self.pk)
174
+
175
+    @property
176
+    def image_path(self):
177
+        return upload_file_path(self.image)
178
+
179
+    @property
180
+    def image_url(self):
181
+        return upload_file_url(self.image)
182
+
183
+    @property
184
+    def cover_path(self):
185
+        return upload_file_path(self.cover)
186
+
187
+    @property
188
+    def cover_url(self):
189
+        return upload_file_url(self.cover)
190
+
191
+    @property
192
+    def slider_image_path(self):
193
+        return upload_file_path(self.slider_image)
194
+
195
+    @property
196
+    def slider_image_url(self):
197
+        return upload_file_url(self.slider_image)
198
+
199
+    @property
200
+    def data(self):
201
+        return {
202
+            'id': self.pk,
203
+            'title': self.title,
204
+            'subtitle': self.subtitle,
205
+            'date': self.date,
206
+            'location': self.location,
207
+            'cover_url': self.cover_url,
208
+            'content_rich_text': self.content_rich_text,
209
+            'state': 0,
210
+            'is_signed': 0,
211
+        }

+ 7 - 0
member/tests.py

@@ -0,0 +1,7 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.test import TestCase
5
+
6
+
7
+# Create your tests here.

+ 7 - 0
member/views.py

@@ -0,0 +1,7 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.shortcuts import render
5
+
6
+
7
+# Create your views here.

+ 1 - 1
pre/views.py

@@ -2,11 +2,11 @@
2 2
 
3 3
 from __future__ import division
4 4
 
5
+import xlrd
5 6
 from django.conf import settings
6 7
 from pysnippets.strsnippets import strip
7 8
 from TimeConvert import TimeConvert as tc
8 9
 
9
-import xlrd
10 10
 from mch.models import BrandInfo, ConsumeInfoSubmitLogInfo, DistributorInfo, ModelInfo
11 11
 from statistic.models import (ConsumeDistributorSaleStatisticInfo, ConsumeModelSaleStatisticInfo,
12 12
                               ConsumeProvinceSaleStatisticInfo, ConsumeSaleStatisticInfo, ConsumeUserStatisticInfo,

+ 4 - 4
requirements.txt

@@ -1,15 +1,15 @@
1 1
 CodeConvert==2.0.5
2
-MySQL-python==1.2.5
3 2
 Pillow==5.0.0
4 3
 StatusCode==1.0.0
5 4
 TimeConvert==1.5.0
6
-furl==2.0.0
5
+furl==2.1.0
7 6
 isoweek==1.3.3
8 7
 jsonfield==2.0.2
9 8
 mock==2.0.0
10 9
 monetary==1.0.3
11
-pysnippets==1.0.8
12
-qiniu==7.2.2
10
+mysqlclient==1.4.5
11
+pysnippets==1.1.2
12
+qiniu==7.2.6
13 13
 requests==2.21.0
14 14
 rlog==0.3
15 15
 rsa==3.4.2

+ 6 - 6
requirements_dj.txt

@@ -1,20 +1,20 @@
1
-Django==1.11.25
1
+Django==1.11.26
2 2
 django-admin==2.0.0
3
-django-cors-headers==2.4.0
3
+django-cors-headers==3.0.2
4 4
 django-curtail-uuid==1.0.4
5
-django-detect==1.0.8
5
+django-detect==1.0.16
6 6
 django-file-md5==1.0.3
7 7
 django-file-upload==1.1.1
8 8
 django-ip==1.0.2
9
-django-json-render==1.0.2
9
+django-json-render==1.0.3
10 10
 django-json-response==1.1.5
11 11
 django-logit==1.1.3
12 12
 django-mobi==0.1.7
13 13
 django-models-ext==1.1.9
14 14
 django-multidomain==1.1.4
15 15
 django-paginator2==1.1.3
16
-django-query==1.0.3
17
-django-redis-connector==1.0.1
16
+django-query==1.0.6
17
+django-redis-connector==1.0.3
18 18
 django-response==1.1.1
19 19
 django-rlog==1.0.7
20 20
 django-shortuuidfield==0.1.3

+ 1 - 1
requirements_pywe.txt

@@ -4,7 +4,7 @@ pywe-jssdk==1.1.0
4 4
 pywe-membercard==1.0.1
5 5
 pywe-miniapp==1.1.5
6 6
 pywe-oauth==1.0.7
7
-pywe-pay==1.0.12
7
+pywe-pay==1.0.13
8 8
 pywe-pay-notify==1.0.4
9 9
 pywe-response==1.0.1
10 10
 pywe-sign==1.1.0

+ 1 - 1
requirements_redis.txt

@@ -1,3 +1,3 @@
1
-hiredis==1.0.0
1
+hiredis==1.0.1
2 2
 redis==2.10.6
3 3
 redis-extensions==1.2.5

+ 0 - 0
simditor/__init__.py


+ 36 - 0
simditor/fields.py

@@ -0,0 +1,36 @@
1
+# -- coding: utf-8 --
2
+"""simditor fields."""
3
+from django import forms
4
+from django.db import models
5
+
6
+from .widgets import SimditorWidget
7
+
8
+
9
+class RichTextFormField(forms.fields.CharField):
10
+    """RichTextFormField."""
11
+
12
+    def __init__(self, *args, **kwargs):
13
+        kwargs.update(
14
+            {
15
+                'widget': SimditorWidget()
16
+            }
17
+        )
18
+        super(RichTextFormField, self).__init__(*args, **kwargs)
19
+
20
+
21
+class RichTextField(models.TextField):
22
+    """RichTextField."""
23
+
24
+    def __init__(self, *args, **kwargs):
25
+        super(RichTextField, self).__init__(*args, **kwargs)
26
+
27
+    def formfield(self, **kwargs):
28
+        defaults = {
29
+            'form_class': self._get_form_class()
30
+        }
31
+        defaults.update(kwargs)
32
+        return super(RichTextField, self).formfield(**defaults)
33
+
34
+    @staticmethod
35
+    def _get_form_class():
36
+        return RichTextFormField

+ 0 - 0
simditor/image/__init__.py


+ 11 - 0
simditor/image/dummy_backend.py

@@ -0,0 +1,11 @@
1
+# -- coding: utf-8 --
2
+"""simditor image pillow_backend."""
3
+from __future__ import absolute_import
4
+
5
+from simditor import utils
6
+
7
+
8
+def image_verify(file_object):
9
+    """image_verify."""
10
+    if not utils.is_valid_image_extension(file_object.name):
11
+        raise utils.NotAnImageException

+ 28 - 0
simditor/image/pillow_backend.py

@@ -0,0 +1,28 @@
1
+# -- coding: utf-8 --
2
+"""simditor image pillow_backend."""
3
+from __future__ import absolute_import
4
+
5
+import os
6
+from io import BytesIO
7
+
8
+from django.core.files.storage import default_storage
9
+from django.core.files.uploadedfile import InMemoryUploadedFile
10
+
11
+from simditor import utils
12
+
13
+
14
+try:
15
+    from PIL import Image, ImageOps
16
+except ImportError:
17
+    import Image
18
+    import ImageOps
19
+
20
+
21
+THUMBNAIL_SIZE = (75, 75)
22
+
23
+
24
+def image_verify(f):
25
+    try:
26
+        Image.open(f).verify()
27
+    except IOError:
28
+        raise utils.NotAnImageException

+ 16 - 0
simditor/image_processing.py

@@ -0,0 +1,16 @@
1
+# -- coding: utf-8 --
2
+"""simditor image_processing."""
3
+from __future__ import absolute_import
4
+
5
+from django.conf import settings
6
+
7
+
8
+def get_backend():
9
+    """Get backend."""
10
+    backend = getattr(settings, 'SIMDITOR_IMAGE_BACKEND', None)
11
+
12
+    if backend == 'pillow':
13
+        from simditor.image import pillow_backend as backend
14
+    else:
15
+        from simditor.image import dummy_backend as backend
16
+    return backend

二进制
simditor/static/simditor/fonts/icomoon.eot


+ 11 - 0
simditor/static/simditor/fonts/icomoon.svg

@@ -0,0 +1,11 @@
1
+<?xml version="1.0" standalone="no"?>
2
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+<svg xmlns="http://www.w3.org/2000/svg">
4
+<metadata>Generated by IcoMoon</metadata>
5
+<defs>
6
+<font id="icomoon" horiz-adv-x="512">
7
+<font-face units-per-em="512" ascent="480" descent="-32" />
8
+<missing-glyph horiz-adv-x="512" />
9
+<glyph unicode="&#x20;" d="" horiz-adv-x="256" />
10
+<glyph unicode="&#xe600;" d="M438.624 86.624l-73.376 73.376-45.248-45.248 73.376-73.376-73.376-73.376h192v192zM192 480h-192v-192l73.376 73.376 72.688-72.624 45.248 45.248-72.688 72.624zM192 114.752l-45.248 45.248-73.376-73.376-73.376 73.376v-192h192l-73.376 73.376zM512 480h-192l73.376-73.376-72.688-72.624 45.248-45.248 72.688 72.624 73.376-73.376z" />
11
+</font></defs></svg>

二进制
simditor/static/simditor/fonts/icomoon.ttf


二进制
simditor/static/simditor/fonts/icomoon.woff


+ 82 - 0
simditor/static/simditor/fonts/selection.json

@@ -0,0 +1,82 @@
1
+{
2
+	"IcoMoonType": "selection",
3
+	"icons": [
4
+		{
5
+			"icon": {
6
+				"paths": [
7
+					"M877.248 786.752l-146.752-146.752-90.496 90.496 146.752 146.752-146.752 146.752h384v-384zM384 0h-384v384l146.752-146.752 145.376 145.248 90.496-90.496-145.376-145.248zM384 730.496l-90.496-90.496-146.752 146.752-146.752-146.752v384h384l-146.752-146.752zM1024 0h-384l146.752 146.752-145.376 145.248 90.496 90.496 145.376-145.248 146.752 146.752z"
8
+				],
9
+				"tags": [
10
+					"fullscreen",
11
+					"expand"
12
+				],
13
+				"grid": 16,
14
+				"attrs": []
15
+			},
16
+			"attrs": [],
17
+			"properties": {
18
+				"id": 99,
19
+				"order": 2,
20
+				"prevSize": 16,
21
+				"code": 58880,
22
+				"name": "fullscreen"
23
+			},
24
+			"setIdx": 1,
25
+			"setId": 6,
26
+			"iconIdx": 99
27
+		}
28
+	],
29
+	"height": 1024,
30
+	"metadata": {
31
+		"name": "icomoon"
32
+	},
33
+	"preferences": {
34
+		"fontPref": {
35
+			"prefix": "icon-",
36
+			"metadata": {
37
+				"fontFamily": "icomoon",
38
+				"majorVersion": 1,
39
+				"minorVersion": 0
40
+			},
41
+			"showGlyphs": true,
42
+			"metrics": {
43
+				"emSize": 512,
44
+				"baseline": 6.25,
45
+				"whitespace": 50
46
+			},
47
+			"resetPoint": 58880,
48
+			"showQuickUse": true,
49
+			"quickUsageToken": false,
50
+			"showMetrics": false,
51
+			"showMetadata": false,
52
+			"autoHost": true,
53
+			"embed": false,
54
+			"ie7": false,
55
+			"showSelector": false,
56
+			"showVersion": true
57
+		},
58
+		"imagePref": {
59
+			"color": 0,
60
+			"height": 32,
61
+			"columns": 16,
62
+			"margin": 16,
63
+			"png": false,
64
+			"sprites": true,
65
+			"prefix": "icon-"
66
+		},
67
+		"historySize": 100,
68
+		"showCodes": true,
69
+		"gridSize": 16,
70
+		"showLiga": false,
71
+		"showGrid": true,
72
+		"showGlyphs": true,
73
+		"showQuickUse": true,
74
+		"search": "",
75
+		"quickUsageToken": {
76
+			"UntitledProject1": "ZWEwOTk2NTRmNjMyOGQ1MzAwZWFiYmJlODViMWMzZDcjMiMxNDA3NzM0MTA2IyMj"
77
+		},
78
+		"showQuickUse2": true,
79
+		"showSVGs": true,
80
+		"fontHostingName": false
81
+	}
82
+}

二进制
simditor/static/simditor/images/emoji/+1.png


二进制
simditor/static/simditor/images/emoji/-1.png


二进制
simditor/static/simditor/images/emoji/100.png


二进制
simditor/static/simditor/images/emoji/109.png


二进制
simditor/static/simditor/images/emoji/1234.png


二进制
simditor/static/simditor/images/emoji/a.png


二进制
simditor/static/simditor/images/emoji/ab.png


二进制
simditor/static/simditor/images/emoji/abc.png


二进制
simditor/static/simditor/images/emoji/abcd.png


二进制
simditor/static/simditor/images/emoji/accept.png


二进制
simditor/static/simditor/images/emoji/aerial_tramway.png


二进制
simditor/static/simditor/images/emoji/airplane.png


二进制
simditor/static/simditor/images/emoji/alarm_clock.png


二进制
simditor/static/simditor/images/emoji/alien.png


二进制
simditor/static/simditor/images/emoji/ambulance.png


二进制
simditor/static/simditor/images/emoji/anchor.png


二进制
simditor/static/simditor/images/emoji/angel.png


二进制
simditor/static/simditor/images/emoji/anger.png


二进制
simditor/static/simditor/images/emoji/angry.png


二进制
simditor/static/simditor/images/emoji/anguished.png


二进制
simditor/static/simditor/images/emoji/ant.png


二进制
simditor/static/simditor/images/emoji/apple.png


二进制
simditor/static/simditor/images/emoji/aquarius.png


二进制
simditor/static/simditor/images/emoji/aries.png


二进制
simditor/static/simditor/images/emoji/arrow_backward.png


二进制
simditor/static/simditor/images/emoji/arrow_double_down.png


二进制
simditor/static/simditor/images/emoji/arrow_double_up.png


二进制
simditor/static/simditor/images/emoji/arrow_down.png


二进制
simditor/static/simditor/images/emoji/arrow_down_small.png


二进制
simditor/static/simditor/images/emoji/arrow_forward.png


二进制
simditor/static/simditor/images/emoji/arrow_heading_down.png


二进制
simditor/static/simditor/images/emoji/arrow_heading_up.png


二进制
simditor/static/simditor/images/emoji/arrow_left.png


二进制
simditor/static/simditor/images/emoji/arrow_lower_left.png


二进制
simditor/static/simditor/images/emoji/arrow_lower_right.png


二进制
simditor/static/simditor/images/emoji/arrow_right.png


二进制
simditor/static/simditor/images/emoji/arrow_right_hook.png


二进制
simditor/static/simditor/images/emoji/arrow_up.png


二进制
simditor/static/simditor/images/emoji/arrow_up_down.png


二进制
simditor/static/simditor/images/emoji/arrow_up_small.png


二进制
simditor/static/simditor/images/emoji/arrow_upper_left.png


二进制
simditor/static/simditor/images/emoji/arrow_upper_right.png


二进制
simditor/static/simditor/images/emoji/arrows_clockwise.png


二进制
simditor/static/simditor/images/emoji/arrows_counterclockwise.png


二进制
simditor/static/simditor/images/emoji/art.png


二进制
simditor/static/simditor/images/emoji/articulated_lorry.png


二进制
simditor/static/simditor/images/emoji/astonished.png


二进制
simditor/static/simditor/images/emoji/atm.png


二进制
simditor/static/simditor/images/emoji/b.png


二进制
simditor/static/simditor/images/emoji/baby.png


二进制
simditor/static/simditor/images/emoji/baby_bottle.png


二进制
simditor/static/simditor/images/emoji/baby_chick.png


二进制
simditor/static/simditor/images/emoji/baby_symbol.png


二进制
simditor/static/simditor/images/emoji/baggage_claim.png


二进制
simditor/static/simditor/images/emoji/balloon.png


二进制
simditor/static/simditor/images/emoji/ballot_box_with_check.png


二进制
simditor/static/simditor/images/emoji/bamboo.png


二进制
simditor/static/simditor/images/emoji/banana.png


二进制
simditor/static/simditor/images/emoji/bangbang.png


二进制
simditor/static/simditor/images/emoji/bank.png


二进制
simditor/static/simditor/images/emoji/bar_chart.png


二进制
simditor/static/simditor/images/emoji/barber.png


二进制
simditor/static/simditor/images/emoji/baseball.png


二进制
simditor/static/simditor/images/emoji/basketball.png


二进制
simditor/static/simditor/images/emoji/bath.png


二进制
simditor/static/simditor/images/emoji/bathtub.png


二进制
simditor/static/simditor/images/emoji/battery.png


二进制
simditor/static/simditor/images/emoji/bear.png


二进制
simditor/static/simditor/images/emoji/beer.png


二进制
simditor/static/simditor/images/emoji/beers.png


二进制
simditor/static/simditor/images/emoji/beetle.png


二进制
simditor/static/simditor/images/emoji/beginner.png


二进制
simditor/static/simditor/images/emoji/bell.png


二进制
simditor/static/simditor/images/emoji/bento.png


二进制
simditor/static/simditor/images/emoji/bicyclist.png


二进制
simditor/static/simditor/images/emoji/bike.png


二进制
simditor/static/simditor/images/emoji/bikini.png


二进制
simditor/static/simditor/images/emoji/bird.png


二进制
simditor/static/simditor/images/emoji/birthday.png


二进制
simditor/static/simditor/images/emoji/black_circle.png


二进制
simditor/static/simditor/images/emoji/black_joker.png


二进制
simditor/static/simditor/images/emoji/black_nib.png


二进制
simditor/static/simditor/images/emoji/black_square.png


二进制
simditor/static/simditor/images/emoji/black_square_button.png


二进制
simditor/static/simditor/images/emoji/blossom.png


二进制
simditor/static/simditor/images/emoji/blowfish.png


二进制
simditor/static/simditor/images/emoji/blue_book.png


二进制
simditor/static/simditor/images/emoji/blue_car.png


二进制
simditor/static/simditor/images/emoji/blue_heart.png


二进制
simditor/static/simditor/images/emoji/blush.png


二进制
simditor/static/simditor/images/emoji/boar.png


二进制
simditor/static/simditor/images/emoji/boat.png


二进制
simditor/static/simditor/images/emoji/bomb.png


二进制
simditor/static/simditor/images/emoji/book.png


二进制
simditor/static/simditor/images/emoji/bookmark.png


二进制
simditor/static/simditor/images/emoji/bookmark_tabs.png


二进制
simditor/static/simditor/images/emoji/books.png


二进制
simditor/static/simditor/images/emoji/boom.png


二进制
simditor/static/simditor/images/emoji/boot.png


二进制
simditor/static/simditor/images/emoji/bouquet.png


二进制
simditor/static/simditor/images/emoji/bow.png


二进制
simditor/static/simditor/images/emoji/bowling.png


二进制
simditor/static/simditor/images/emoji/bowtie.png


二进制
simditor/static/simditor/images/emoji/boy.png


二进制
simditor/static/simditor/images/emoji/bread.png


二进制
simditor/static/simditor/images/emoji/bride_with_veil.png


二进制
simditor/static/simditor/images/emoji/bridge_at_night.png


二进制
simditor/static/simditor/images/emoji/briefcase.png


二进制
simditor/static/simditor/images/emoji/broken_heart.png


二进制
simditor/static/simditor/images/emoji/bug.png


二进制
simditor/static/simditor/images/emoji/bulb.png


二进制
simditor/static/simditor/images/emoji/bullettrain_front.png


二进制
simditor/static/simditor/images/emoji/bullettrain_side.png


二进制
simditor/static/simditor/images/emoji/bus.png


二进制
simditor/static/simditor/images/emoji/busstop.png


二进制
simditor/static/simditor/images/emoji/bust_in_silhouette.png


二进制
simditor/static/simditor/images/emoji/busts_in_silhouette.png


二进制
simditor/static/simditor/images/emoji/cactus.png


二进制
simditor/static/simditor/images/emoji/cake.png


二进制
simditor/static/simditor/images/emoji/calendar.png


二进制
simditor/static/simditor/images/emoji/calling.png


二进制
simditor/static/simditor/images/emoji/camel.png


二进制
simditor/static/simditor/images/emoji/camera.png


二进制
simditor/static/simditor/images/emoji/cancer.png


二进制
simditor/static/simditor/images/emoji/candy.png


二进制
simditor/static/simditor/images/emoji/capital_abcd.png


二进制
simditor/static/simditor/images/emoji/capricorn.png


二进制
simditor/static/simditor/images/emoji/car.png


二进制
simditor/static/simditor/images/emoji/card_index.png


二进制
simditor/static/simditor/images/emoji/carousel_horse.png


二进制
simditor/static/simditor/images/emoji/cat.png


二进制
simditor/static/simditor/images/emoji/cat2.png


二进制
simditor/static/simditor/images/emoji/cd.png


二进制
simditor/static/simditor/images/emoji/chart.png


二进制
simditor/static/simditor/images/emoji/chart_with_downwards_trend.png


二进制
simditor/static/simditor/images/emoji/chart_with_upwards_trend.png


二进制
simditor/static/simditor/images/emoji/checkered_flag.png


二进制
simditor/static/simditor/images/emoji/cherries.png


二进制
simditor/static/simditor/images/emoji/cherry_blossom.png


二进制
simditor/static/simditor/images/emoji/chestnut.png


二进制
simditor/static/simditor/images/emoji/chicken.png


二进制
simditor/static/simditor/images/emoji/children_crossing.png


二进制
simditor/static/simditor/images/emoji/chocolate_bar.png


二进制
simditor/static/simditor/images/emoji/christmas_tree.png


二进制
simditor/static/simditor/images/emoji/church.png


二进制
simditor/static/simditor/images/emoji/cinema.png


二进制
simditor/static/simditor/images/emoji/circus_tent.png


二进制
simditor/static/simditor/images/emoji/city_sunrise.png


二进制
simditor/static/simditor/images/emoji/city_sunset.png


二进制
simditor/static/simditor/images/emoji/cl.png


二进制
simditor/static/simditor/images/emoji/clap.png


二进制
simditor/static/simditor/images/emoji/clapper.png


二进制
simditor/static/simditor/images/emoji/clipboard.png


二进制
simditor/static/simditor/images/emoji/clock1.png


二进制
simditor/static/simditor/images/emoji/clock10.png


二进制
simditor/static/simditor/images/emoji/clock1030.png


二进制
simditor/static/simditor/images/emoji/clock11.png


二进制
simditor/static/simditor/images/emoji/clock1130.png


二进制
simditor/static/simditor/images/emoji/clock12.png


二进制
simditor/static/simditor/images/emoji/clock1230.png


二进制
simditor/static/simditor/images/emoji/clock130.png


二进制
simditor/static/simditor/images/emoji/clock2.png


二进制
simditor/static/simditor/images/emoji/clock230.png


二进制
simditor/static/simditor/images/emoji/clock3.png


二进制
simditor/static/simditor/images/emoji/clock330.png


二进制
simditor/static/simditor/images/emoji/clock4.png


二进制
simditor/static/simditor/images/emoji/clock430.png


二进制
simditor/static/simditor/images/emoji/clock5.png


二进制
simditor/static/simditor/images/emoji/clock530.png


二进制
simditor/static/simditor/images/emoji/clock6.png


二进制
simditor/static/simditor/images/emoji/clock630.png


二进制
simditor/static/simditor/images/emoji/clock7.png


二进制
simditor/static/simditor/images/emoji/clock730.png


二进制
simditor/static/simditor/images/emoji/clock8.png


二进制
simditor/static/simditor/images/emoji/clock830.png


二进制
simditor/static/simditor/images/emoji/clock9.png


二进制
simditor/static/simditor/images/emoji/clock930.png


二进制
simditor/static/simditor/images/emoji/closed_book.png


二进制
simditor/static/simditor/images/emoji/closed_lock_with_key.png


二进制
simditor/static/simditor/images/emoji/closed_umbrella.png


二进制
simditor/static/simditor/images/emoji/cloud.png


二进制
simditor/static/simditor/images/emoji/clubs.png


二进制
simditor/static/simditor/images/emoji/cn.png


二进制
simditor/static/simditor/images/emoji/cocktail.png


二进制
simditor/static/simditor/images/emoji/coffee.png


二进制
simditor/static/simditor/images/emoji/cold_sweat.png


二进制
simditor/static/simditor/images/emoji/collision.png


二进制
simditor/static/simditor/images/emoji/computer.png


二进制
simditor/static/simditor/images/emoji/confetti_ball.png


二进制
simditor/static/simditor/images/emoji/confounded.png


二进制
simditor/static/simditor/images/emoji/confused.png


二进制
simditor/static/simditor/images/emoji/congratulations.png


二进制
simditor/static/simditor/images/emoji/construction.png


二进制
simditor/static/simditor/images/emoji/construction_worker.png


二进制
simditor/static/simditor/images/emoji/convenience_store.png


二进制
simditor/static/simditor/images/emoji/cookie.png


二进制
simditor/static/simditor/images/emoji/cool.png


二进制
simditor/static/simditor/images/emoji/cop.png


二进制
simditor/static/simditor/images/emoji/copyright.png


二进制
simditor/static/simditor/images/emoji/corn.png


二进制
simditor/static/simditor/images/emoji/couple.png


二进制
simditor/static/simditor/images/emoji/couple_with_heart.png


二进制
simditor/static/simditor/images/emoji/couplekiss.png


二进制
simditor/static/simditor/images/emoji/cow.png


二进制
simditor/static/simditor/images/emoji/cow2.png


二进制
simditor/static/simditor/images/emoji/credit_card.png


二进制
simditor/static/simditor/images/emoji/crocodile.png


二进制
simditor/static/simditor/images/emoji/crossed_flags.png


二进制
simditor/static/simditor/images/emoji/crown.png


二进制
simditor/static/simditor/images/emoji/cry.png


二进制
simditor/static/simditor/images/emoji/crying_cat_face.png


二进制
simditor/static/simditor/images/emoji/crystal_ball.png


二进制
simditor/static/simditor/images/emoji/cupid.png


二进制
simditor/static/simditor/images/emoji/curly_loop.png


二进制
simditor/static/simditor/images/emoji/currency_exchange.png


二进制
simditor/static/simditor/images/emoji/curry.png


二进制
simditor/static/simditor/images/emoji/custard.png


二进制
simditor/static/simditor/images/emoji/customs.png


二进制
simditor/static/simditor/images/emoji/cyclone.png


二进制
simditor/static/simditor/images/emoji/dancer.png


二进制
simditor/static/simditor/images/emoji/dancers.png


二进制
simditor/static/simditor/images/emoji/dango.png


二进制
simditor/static/simditor/images/emoji/dart.png


二进制
simditor/static/simditor/images/emoji/dash.png


二进制
simditor/static/simditor/images/emoji/date.png


二进制
simditor/static/simditor/images/emoji/de.png


二进制
simditor/static/simditor/images/emoji/deciduous_tree.png


二进制
simditor/static/simditor/images/emoji/department_store.png


二进制
simditor/static/simditor/images/emoji/diamond_shape_with_a_dot_inside.png


二进制
simditor/static/simditor/images/emoji/diamonds.png


二进制
simditor/static/simditor/images/emoji/disappointed.png


二进制
simditor/static/simditor/images/emoji/dizzy.png


二进制
simditor/static/simditor/images/emoji/dizzy_face.png


二进制
simditor/static/simditor/images/emoji/do_not_litter.png


二进制
simditor/static/simditor/images/emoji/dog.png


二进制
simditor/static/simditor/images/emoji/dog2.png


二进制
simditor/static/simditor/images/emoji/dollar.png


二进制
simditor/static/simditor/images/emoji/dolls.png


二进制
simditor/static/simditor/images/emoji/dolphin.png


二进制
simditor/static/simditor/images/emoji/door.png


二进制
simditor/static/simditor/images/emoji/doughnut.png


二进制
simditor/static/simditor/images/emoji/dragon.png


二进制
simditor/static/simditor/images/emoji/dragon_face.png


二进制
simditor/static/simditor/images/emoji/dress.png


二进制
simditor/static/simditor/images/emoji/dromedary_camel.png


二进制
simditor/static/simditor/images/emoji/droplet.png


二进制
simditor/static/simditor/images/emoji/dvd.png


二进制
simditor/static/simditor/images/emoji/e-mail.png


二进制
simditor/static/simditor/images/emoji/ear.png


二进制
simditor/static/simditor/images/emoji/ear_of_rice.png


二进制
simditor/static/simditor/images/emoji/earth_africa.png


二进制
simditor/static/simditor/images/emoji/earth_americas.png


二进制
simditor/static/simditor/images/emoji/earth_asia.png


二进制
simditor/static/simditor/images/emoji/egg.png


二进制
simditor/static/simditor/images/emoji/eggplant.png


二进制
simditor/static/simditor/images/emoji/eight.png


二进制
simditor/static/simditor/images/emoji/eight_pointed_black_star.png


二进制
simditor/static/simditor/images/emoji/eight_spoked_asterisk.png


二进制
simditor/static/simditor/images/emoji/eightball.png


二进制
simditor/static/simditor/images/emoji/electric_plug.png


二进制
simditor/static/simditor/images/emoji/elephant.png


二进制
simditor/static/simditor/images/emoji/email.png


+ 0 - 0
simditor/static/simditor/images/emoji/emojify.css

@@ -0,0 +1 @@

change some info store in redis & remove some unnecessary params check & add some return for some api · 454f8cd8ec - Gogs: Go Git Service

change some info store in redis & remove some unnecessary params check & add some return for some api

Brightcells 10 anni fa
parent
commit
454f8cd8ec

+ 2 - 3
account/models.py

@@ -138,15 +138,14 @@ class UserInfo(CreateUpdateMixin):
138 138
         elif self.user_from == self.WX_USER:
139 139
             return self.nickname
140 140
 
141
-    def _data(self):
141
+    @property
142
+    def data(self):
142 143
         return {
143 144
             'user_id': self.user_id,
144 145
             'username': self.username,
145 146
             'nickname': self.nickname,
146 147
         }
147 148
 
148
-    data = property(_data)
149
-
150 149
 
151 150
 class UserLoginLogInfo(CreateUpdateMixin):
152 151
     SUCCESS = 0

+ 1 - 1
account/views.py

@@ -11,7 +11,7 @@ from account.serializers import UserSerializer, GroupSerializer, LensmanInfoSeri
11 11
 
12 12
 from utils.ip_utils import ip_addr
13 13
 
14
-from utils.error.errno_utils import LensmanStatusCode
14
+from utils.error.errno_utils import LensmanStatusCode, UserStatusCode
15 15
 from utils.error.response_utils import response
16 16
 
17 17
 from curtail_uuid import CurtailUUID

+ 2 - 2
api/urls.py

@@ -30,8 +30,8 @@ urlpatterns += [
30 30
     url(r'^g/lock$', group_views.group_lock_api, name='group_lock_api'),  # 群组锁定
31 31
     url(r'^g/unlock$', group_views.group_unlock_api, name='group_unlock_api'),  # 群组解锁
32 32
     url(r'^g/remove$', group_views.group_remove_api, name='group_remove_api'),  # 成员移除
33
-    url(r'^g/pass$', group_views.group_pass_api, name='group_pass_api'),  # 申请通过
34
-    url(r'^g/refuse$', group_views.group_refuse_api, name='group_refuse_api'),  # 申请拒绝
33
+    # url(r'^g/pass$', group_views.group_pass_api, name='group_pass_api'),  # 申请通过
34
+    # url(r'^g/refuse$', group_views.group_refuse_api, name='group_refuse_api'),  # 申请拒绝
35 35
 ]
36 36
 
37 37
 # 飞图相关

+ 3 - 2
group/models.py

@@ -43,6 +43,7 @@ class GroupInfo(CreateUpdateMixin):
43 43
     def data(self):
44 44
         return {
45 45
             'group_id': self.group_id,
46
+            'admin_id': self.admin_id,
46 47
             'group_name': self.group_name,
47 48
             'group_default_avatar': self.group_default_avatar,
48 49
             'group_avatar': self.group_avatar_url,
@@ -52,14 +53,14 @@ class GroupInfo(CreateUpdateMixin):
52 53
             'created_at': self.created_at,
53 54
         }
54 55
 
55
-    def users(self, user_id):
56
+    def users(self, admin=True, user_id=None):
56 57
         all_users = GroupUserInfo.objects.filter(group_id=self.group_id)
57 58
 
58 59
         passed_users = all_users.filter(user_status=GroupUserInfo.PASSED)
59 60
         passed_count = passed_users.count()
60 61
         passed = [passed.user_info for passed in passed_users]
61 62
 
62
-        if self.admin_id != user_id:
63
+        if admin and self.admin_id != user_id:
63 64
             return {
64 65
                 'passed_count': passed_count,
65 66
                 'passed': passed,

+ 276 - 174
group/views.py

@@ -20,6 +20,10 @@ from utils.url_utils import img_url
20 20
 from utils.error.errno_utils import UserStatusCode, GroupStatusCode, GroupUserStatusCode, GroupPhotoStatusCode
21 21
 from utils.error.response_utils import response
22 22
 
23
+from utils.redis.rkeys import GROUP_USERS_APPLYING_SET, GROUP_USERS_PASSED_SET, GROUP_USERS_REFUSED_SET, GROUP_USERS_DELETED_SET
24
+from utils.redis.rkeys import GROUP_LAST_PHOTO_PK
25
+from utils.redis.rgroup import set_group_info, get_group_info, set_group_users_info, get_group_users_info
26
+
23 27
 from utils.sql.raw import PAI2_HOME_API
24 28
 
25 29
 from curtail_uuid import CurtailUUID
@@ -35,16 +39,25 @@ r = settings.REDIS_CACHE
35 39
 
36 40
 @transaction.atomic
37 41
 def group_create_api(request):
42
+    """
43
+    群组创建
44
+    :param request:
45
+    :return:
46
+    """
38 47
     user_id = request.POST.get('user_id', '')
39 48
     group_name = request.POST.get('group_name', '')
40 49
     group_default_avatar = int(request.POST.get('group_default_avatar', 0))
41 50
 
51
+    # 用户校验
42 52
     try:
43 53
         user = UserInfo.objects.get(user_id=user_id)
44 54
     except UserInfo.DoesNotExist:
45 55
         return response(UserStatusCode.USER_NOT_FOUND)
46 56
 
57
+    # 群组唯一标识
47 58
     group_id = CurtailUUID.uuid(GroupInfo, 'group_id')
59
+
60
+    # 群组记录创建
48 61
     group = GroupInfo.objects.create(
49 62
         group_id=group_id,
50 63
         admin_id=user_id,
@@ -52,6 +65,11 @@ def group_create_api(request):
52 65
         group_default_avatar=group_default_avatar,
53 66
         group_from=GroupInfo.APP_GROUP,
54 67
     )
68
+
69
+    # Redis 群组数据缓存
70
+    group_info = set_group_info(group)
71
+
72
+    # 群组用户记录创建
55 73
     GroupUserInfo.objects.create(
56 74
         group_id=group_id,
57 75
         user_id=user_id,
@@ -62,50 +80,46 @@ def group_create_api(request):
62 80
         passed_at=tc.utc_datetime(),
63 81
     )
64 82
 
83
+    # Redis 群组用户数据缓存
84
+    group_users = set_group_users_info(group)
85
+
65 86
     return JsonResponse({
66 87
         'status': 200,
67 88
         'message': u'群组创建成功',
68 89
         'data': {
69 90
             'group_id': group_id,
70
-            'group': group.data,
71
-            'users': group.users(user_id),
91
+            'group': group_info,
92
+            'users': group_users,
72 93
         },
73 94
     })
74 95
 
75 96
 
76 97
 def group_detail_api(request):
98
+    """
99
+    群组详情
100
+    :param request:
101
+    :return:
102
+    """
77 103
     group_id = request.POST.get('group_id', '')
78 104
     user_id = request.POST.get('user_id', '')
79 105
 
80
-    try:
81
-        user = UserInfo.objects.get(user_id=user_id)
82
-    except UserInfo.DoesNotExist:
83
-        return response(UserStatusCode.USER_NOT_FOUND)
84
-
85
-    try:
86
-        group = GroupInfo.objects.get(group_id=group_id)
87
-    except GroupInfo.DoesNotExist:
88
-        return response(GroupStatusCode.GROUP_NOT_FOUND)
89
-
90
-    # try:
91
-    #     group_user = GroupUserInfo.objects.filter(group_id=group_id, user_id=user_id)
92
-    # except GroupUserInfo.DoesNotExist:
93
-    #     return response(GroupUserStatusCode.GROUP_USER_NOT_FOUND)
94
-    # if group_user.user_status == GroupUserInfo.DELETED:
95
-    #     return response(GroupUserStatusCode.GROUP_USER_HAS_DELETED)
96
-
97 106
     return JsonResponse({
98 107
         'status': 200,
99 108
         'message': u'获取群组详情成功',
100 109
         'data': {
101 110
             'group_id': group_id,
102
-            'group': group.data,
103
-            'users': group.users(user_id),
111
+            'group': get_group_info(group_id),
112
+            'users': get_group_users_info(group_id, user_id),
104 113
         },
105 114
     })
106 115
 
107 116
 
108 117
 def group_update_api(request):
118
+    """
119
+    群组更新
120
+    :param request:
121
+    :return:
122
+    """
109 123
     group_id = request.POST.get('group_id', '')
110 124
     admin_id = request.POST.get('admin_id', '') or request.POST.get('user_id', '')
111 125
     group_name = request.POST.get('group_name', '')
@@ -113,21 +127,23 @@ def group_update_api(request):
113 127
 
114 128
     group_avatar = request.FILES.get('group_avatar', '')
115 129
 
116
-    try:
117
-        admin = UserInfo.objects.get(user_id=admin_id)
118
-    except UserInfo.DoesNotExist:
119
-        return response(UserStatusCode.USER_NOT_FOUND)
120
-
130
+    # 群组校验
121 131
     try:
122 132
         group = GroupInfo.objects.get(group_id=group_id)
123 133
     except GroupInfo.DoesNotExist:
124 134
         return response(GroupStatusCode.GROUP_NOT_FOUND)
135
+
136
+    # 权限校验
125 137
     if group.admin_id != admin_id:
126 138
         return response(GroupStatusCode.NO_UPDATE_PERMISSION)
139
+
140
+    # 群组名称更新
127 141
     if group_name:
128 142
         group.group_name = group_name
143
+    # 群组描述更新
129 144
     if group_desc:
130 145
         group.group_desc = group_desc
146
+    # 群组头像更新
131 147
     if group_avatar:
132 148
         _, extension = os.path.splitext(group_avatar.name)
133 149
         group_avatar_path = 'group/{uuid}_{extension}'.format(uuid=shortuuid.uuid(), extension=extension)
@@ -137,27 +153,33 @@ def group_update_api(request):
137 153
         group.group_avatar = group_avatar_path
138 154
     group.save()
139 155
 
156
+    # Redis 群组数据缓存更新
157
+    group_info = set_group_info(group)
158
+
140 159
     return JsonResponse({
141 160
         'status': 200,
142
-        'message': u'用户更新成功',
161
+        'message': u'群组更新成功',
143 162
         'data': {
144 163
             'group_id': group_id,
145
-            'group': group.data,
146
-            'users': group.users(admin_id),
164
+            'group': group_info,
165
+            'users': get_group_users_info(group_id, admin_id),
147 166
         },
148 167
     })
149 168
 
150 169
 
151 170
 def group_list_api(request):
171
+    """
172
+    群组列表
173
+    :param request:
174
+    :return:
175
+    """
152 176
     user_id = request.POST.get('user_id', '')
153 177
 
154 178
     group_users = GroupUserInfo.objects.filter(user_id=user_id, user_status=GroupUserInfo.PASSED)
155 179
     groups = []
156 180
     for group_user in group_users:
157
-        try:
158
-            groups.append(GroupInfo.objects.get(group_id=group_user.group_id).data)
159
-        except GroupInfo.DoesNotExist:
160
-            pass
181
+        group_info = get_group_info(group_user.group_id)
182
+        groups.append(group_info) if group_info else None
161 183
 
162 184
     return JsonResponse({
163 185
         'status': 200,
@@ -169,31 +191,40 @@ def group_list_api(request):
169 191
 
170 192
 
171 193
 def group_join_api(request):
194
+    """
195
+    申请加群
196
+    :param request:
197
+    :return:
198
+    """
172 199
     group_id = request.POST.get('group_id', '')
173 200
     user_id = request.POST.get('user_id', '')
174 201
     nickname = request.POST.get('nickname', '')
175 202
 
203
+    # 用户校验
176 204
     try:
177 205
         user = UserInfo.objects.get(user_id=user_id)
178 206
     except UserInfo.DoesNotExist:
179 207
         return response(UserStatusCode.USER_NOT_FOUND)
180 208
 
209
+    # 群组校验
181 210
     try:
182 211
         group = GroupInfo.objects.get(group_id=group_id)
183 212
     except GroupInfo.DoesNotExist:
184 213
         return response(GroupStatusCode.GROUP_NOT_FOUND)
214
+
215
+    # 群组锁定校验
185 216
     if group.group_lock:
186 217
         return response(GroupStatusCode.GROUP_HAS_LOCKED)
187 218
 
188
-    if GroupUserInfo.objects.filter(group_id=group_id, user_id=user_id, user_status__in=[GroupUserInfo.APPLYING, GroupUserInfo.PASSED]).exists():
219
+    # 重复申请校验
220
+    if r.sismember(GROUP_USERS_APPLYING_SET % group_id, user_id) or r.sismember(GROUP_USERS_PASSED_SET % group_id, user_id):
189 221
         return response(GroupStatusCode.DUPLICATE_JOIN_REQUEST)
190 222
 
191
-    group_photo = GroupPhotoInfo.objects.filter(group_id=group_id).last()
192
-
223
+    # 群组用户记录创建
193 224
     GroupUserInfo.objects.create(
194 225
         group_id=group_id,
195 226
         user_id=user_id,
196
-        current_id=group_photo and group_photo.id or -1,
227
+        current_id=int(r.get(GROUP_LAST_PHOTO_PK % group_id) or -1),
197 228
         nickname=nickname or user.final_nickname,
198 229
         avatar=user.avatar,
199 230
         admin=False,
@@ -201,34 +232,49 @@ def group_join_api(request):
201 232
         passed_at=tc.utc_datetime(),
202 233
     )
203 234
 
235
+    # Redis 群组用户数据缓存
236
+    set_group_users_info(group)
237
+
238
+    # Redis 群组通过集合缓存
239
+    r.sadd(GROUP_USERS_PASSED_SET % group_id, user_id)
240
+
204 241
     return JsonResponse({
205 242
         'status': 200,
206
-        'message': u'申请已提交',
243
+        'message': u'申请成功',
244
+        'data': {
245
+            'group_id': group_id,
246
+            'group': get_group_info(group_id),
247
+            'users': get_group_users_info(group_id, user_id),
248
+        },
207 249
     })
208 250
 
209 251
 
210 252
 def group_lock_api(request):
253
+    """
254
+    群组锁定
255
+    :param request:
256
+    :return:
257
+    """
211 258
     group_id = request.POST.get('group_id', '')
212
-    user_id = request.POST.get('user_id', '')
213
-
214
-    try:
215
-        user = UserInfo.objects.get(user_id=user_id)
216
-    except UserInfo.DoesNotExist:
217
-        return response(UserStatusCode.USER_NOT_FOUND)
259
+    admin_id = request.POST.get('admin_id', '') or request.POST.get('user_id', '')
218 260
 
261
+    # 群组校验
219 262
     try:
220 263
         group = GroupInfo.objects.get(group_id=group_id)
221 264
     except GroupInfo.DoesNotExist:
222 265
         return response(GroupStatusCode.GROUP_NOT_FOUND)
223
-    if group.admin_id != user_id:
224
-        return JsonResponse({
225
-            'status': 40221,
226
-            'message': u'没有锁定权限',
227
-        })
228 266
 
267
+    # 权限校验
268
+    if group.admin_id != admin_id:
269
+        return response(GroupStatusCode.NO_LOCK_PERMISSION)
270
+
271
+    # 群组锁定
229 272
     group.group_lock = True
230 273
     group.save()
231 274
 
275
+    # Redis 群组数据缓存更新
276
+    set_group_info(group)
277
+
232 278
     return JsonResponse({
233 279
         'status': 200,
234 280
         'message': u'锁定成功',
@@ -236,24 +282,31 @@ def group_lock_api(request):
236 282
 
237 283
 
238 284
 def group_unlock_api(request):
285
+    """
286
+    群组解锁
287
+    :param request:
288
+    :return:
289
+    """
239 290
     group_id = request.POST.get('group_id', '')
240
-    user_id = request.POST.get('user_id', '')
241
-
242
-    try:
243
-        user = UserInfo.objects.get(user_id=user_id)
244
-    except UserInfo.DoesNotExist:
245
-        return response(UserStatusCode.USER_NOT_FOUND)
291
+    admin_id = request.POST.get('admin_id', '') or request.POST.get('user_id', '')
246 292
 
293
+    # 群组校验
247 294
     try:
248 295
         group = GroupInfo.objects.get(group_id=group_id)
249 296
     except GroupInfo.DoesNotExist:
250 297
         return response(GroupStatusCode.GROUP_NOT_FOUND)
251
-    if group.admin_id != user_id:
298
+
299
+    # 权限校验
300
+    if group.admin_id != admin_id:
252 301
         return response(GroupStatusCode.NO_UNLOCK_PERMISSION)
253 302
 
303
+    # 群组解锁
254 304
     group.group_lock = False
255 305
     group.save()
256 306
 
307
+    # Redis 群组数据缓存更新
308
+    set_group_info(group)
309
+
257 310
     return JsonResponse({
258 311
         'status': 200,
259 312
         'message': u'解锁成功',
@@ -261,126 +314,155 @@ def group_unlock_api(request):
261 314
 
262 315
 
263 316
 def group_remove_api(request):
317
+    """
318
+    成员移除
319
+    :param request:
320
+    :return:
321
+    """
264 322
     group_id = request.POST.get('group_id', '')
265 323
     admin_id = request.POST.get('admin_id', '')
266 324
     user_id = request.POST.get('user_id', '')
267 325
 
268
-    try:
269
-        admin = UserInfo.objects.get(user_id=admin_id)
270
-    except UserInfo.DoesNotExist:
271
-        return response(UserStatusCode.USER_NOT_FOUND)
272
-    try:
273
-        user = UserInfo.objects.get(user_id=user_id)
274
-    except UserInfo.DoesNotExist:
275
-        return response(UserStatusCode.USER_NOT_FOUND)
276
-
326
+    # 群组校验
277 327
     try:
278 328
         group = GroupInfo.objects.get(group_id=group_id)
279 329
     except GroupInfo.DoesNotExist:
280 330
         return response(GroupStatusCode.GROUP_NOT_FOUND)
331
+
332
+    # 权限校验
281 333
     if group.admin_id != admin_id:
282
-        return JsonResponse({
283
-            'status': 40222,
284
-            'message': u'没有移除权限',
285
-        })
334
+        return response(GroupStatusCode.NO_REMOVE_PERMISSION)
286 335
 
336
+    # 群组用户校验
287 337
     try:
288 338
         group_user = GroupUserInfo.objects.get(group_id=group_id, user_id=user_id, user_status=GroupUserInfo.PASSED)
289 339
     except GroupUserInfo.DoesNotExist:
290
-        return response(GroupStatusCode.GROUP_USER_NOT_FOUND)
340
+        return response(GroupUserStatusCode.GROUP_USER_NOT_FOUND)
341
+
342
+    # 群组用户移除
291 343
     group_user.user_status = GroupUserInfo.DELETED
292 344
     group_user.deleted_at = tc.utc_datetime()
293 345
     group_user.save()
294 346
 
347
+    # Redis 群组数据缓存更新
348
+    group_users = set_group_info(group)
349
+
350
+    # Redis 群组删除集合缓存
351
+    r.srem(GROUP_USERS_PASSED_SET % group_id, user_id)
352
+    r.sadd(GROUP_USERS_DELETED_SET % group_id, user_id)
353
+
295 354
     return JsonResponse({
296 355
         'status': 200,
297 356
         'message': u'用户移除成功',
298 357
         'data': {
299 358
             'group_id': group_id,
300
-            'users': group.users(admin_id),
359
+            'users': group_users,
301 360
         },
302 361
     })
303 362
 
304 363
 
305 364
 def group_pass_api(request):
365
+    """
366
+    申请通过
367
+    :param request:
368
+    :return:
369
+    """
306 370
     group_id = request.POST.get('group_id', '')
307 371
     admin_id = request.POST.get('admin_id', '')
308 372
     user_id = request.POST.get('user_id', '')
309 373
 
310
-    try:
311
-        admin = UserInfo.objects.get(user_id=admin_id)
312
-    except UserInfo.DoesNotExist:
313
-        return response(UserStatusCode.USER_NOT_FOUND)
314
-    try:
315
-        user = UserInfo.objects.get(user_id=user_id)
316
-    except UserInfo.DoesNotExist:
317
-        return response(UserStatusCode.USER_NOT_FOUND)
318
-
374
+    # 群组校验
319 375
     try:
320 376
         group = GroupInfo.objects.get(group_id=group_id)
321 377
     except GroupInfo.DoesNotExist:
322 378
         return response(GroupStatusCode.GROUP_NOT_FOUND)
379
+
380
+    # 权限校验
323 381
     if group.admin_id != admin_id:
324 382
         return response(GroupStatusCode.NO_PASS_PERMISSION)
325 383
 
384
+    # 群组用户校验
326 385
     try:
327 386
         group_user = GroupUserInfo.objects.get(group_id=group_id, user_id=user_id, user_status=GroupUserInfo.APPLYING)
328 387
     except GroupUserInfo.DoesNotExist:
329 388
         return response(GroupStatusCode.JOIN_REQUEST_NOT_FOUND)
389
+
390
+    # 群组用户通过
330 391
     group_user.user_status = GroupUserInfo.PASSED
331 392
     group_user.passed_at = tc.utc_datetime()
332 393
     group_user.save()
333 394
 
395
+    # Redis 群组数据缓存更新
396
+    group_users = set_group_info(group)
397
+
398
+    # Redis 群组通过集合缓存
399
+    r.srem(GROUP_USERS_APPLYING_SET % group_id, user_id)
400
+    r.sadd(GROUP_USERS_PASSED_SET % group_id, user_id)
401
+
334 402
     return JsonResponse({
335 403
         'status': 200,
336 404
         'message': u'申请通过成功',
337 405
         'data': {
338 406
             'group_id': group_id,
339
-            'users': group.users(admin_id),
407
+            'users': group_users,
340 408
         },
341 409
     })
342 410
 
343 411
 
344 412
 def group_refuse_api(request):
413
+    """
414
+    申请拒绝
415
+    :param request:
416
+    :return:
417
+    """
345 418
     group_id = request.POST.get('group_id', '')
346 419
     admin_id = request.POST.get('admin_id', '')
347 420
     user_id = request.POST.get('user_id', '')
348 421
 
349
-    try:
350
-        admin = UserInfo.objects.get(user_id=admin_id)
351
-    except UserInfo.DoesNotExist:
352
-        return response(UserStatusCode.USER_NOT_FOUND)
353
-    try:
354
-        user = UserInfo.objects.get(user_id=user_id)
355
-    except UserInfo.DoesNotExist:
356
-        return response(UserStatusCode.USER_NOT_FOUND)
357
-
422
+    # 群组校验
358 423
     try:
359 424
         group = GroupInfo.objects.get(group_id=group_id)
360 425
     except GroupInfo.DoesNotExist:
361 426
         return response(GroupStatusCode.GROUP_NOT_FOUND)
427
+
428
+    # 权限校验
362 429
     if group.admin_id != admin_id:
363 430
         return response(GroupStatusCode.NO_REFUSE_PERMISSION)
364 431
 
432
+    # 群组用户校验
365 433
     try:
366 434
         group_user = GroupUserInfo.objects.get(group_id=group_id, user_id=user_id, user_status=GroupUserInfo.APPLYING)
367 435
     except GroupUserInfo.DoesNotExist:
368 436
         return response(GroupStatusCode.JOIN_REQUEST_NOT_FOUND)
437
+
438
+    # 群组用户拒绝
369 439
     group_user.user_status = GroupUserInfo.REFUSED
370 440
     group_user.refused_at = tc.utc_datetime()
371 441
     group_user.save()
372 442
 
443
+    # Redis 群组数据缓存更新
444
+    group_users = set_group_info(group)
445
+
446
+    # Redis 群组拒绝集合缓存
447
+    r.srem(GROUP_USERS_APPLYING_SET % group_id, user_id)
448
+    r.sadd(GROUP_USERS_REFUSED_SET % group_id, user_id)
449
+
373 450
     return JsonResponse({
374 451
         'status': 200,
375 452
         'message': u'申请拒绝成功',
376 453
         'data': {
377 454
             'group_id': group_id,
378
-            'users': group.users(admin_id),
455
+            'users': group_users,
379 456
         },
380 457
     })
381 458
 
382 459
 
383 460
 def flyimg_upload_api(request):
461
+    """
462
+    飞图上传/飞图列表
463
+    :param request:
464
+    :return:
465
+    """
384 466
     group_id = request.POST.get('group_id', '')
385 467
     user_id = request.POST.get('user_id', '')
386 468
     nickname = request.POST.get('nickname', '')
@@ -389,20 +471,17 @@ def flyimg_upload_api(request):
389 471
 
390 472
     current_id = int(request.POST.get('current_id', -1))
391 473
 
474
+    # 用户校验
392 475
     try:
393 476
         user = UserInfo.objects.get(user_id=user_id)
394 477
     except UserInfo.DoesNotExist:
395 478
         return response(UserStatusCode.USER_NOT_FOUND)
396 479
 
397
-    try:
398
-        group = GroupInfo.objects.get(group_id=group_id)
399
-    except GroupInfo.DoesNotExist:
400
-        return response(GroupStatusCode.GROUP_NOT_FOUND)
401
-
480
+    # 群组用户校验
402 481
     try:
403 482
         group_user = GroupUserInfo.objects.get(group_id=group_id, user_id=user_id, user_status=GroupUserInfo.PASSED)
404 483
     except GroupUserInfo.DoesNotExist:
405
-        return response(GroupStatusCode.GROUP_USER_NOT_FOUND)
484
+        return response(GroupUserStatusCode.GROUP_USER_NOT_FOUND)
406 485
 
407 486
     if photo:
408 487
         _, extension = os.path.splitext(photo.name)
@@ -424,6 +503,7 @@ def flyimg_upload_api(request):
424 503
             settings.THUMBNAIL_MAX_WIDTH
425 504
         )
426 505
 
506
+        # 群组照片记录创建
427 507
         group_photo = GroupPhotoInfo.objects.create(
428 508
             group_id=group_id,
429 509
             user_id=user_id,
@@ -437,6 +517,10 @@ def flyimg_upload_api(request):
437 517
             photo_thumbnail_h=photo_thumbnail_h,
438 518
         )
439 519
 
520
+        # 设置群组最后一张照片PK
521
+        r.set(GROUP_LAST_PHOTO_PK % group_id, group_photo.pk)
522
+
523
+    # 获取从 current_id 到 now 的群组照片列表
440 524
     group_photos = GroupPhotoInfo.objects.filter(
441 525
         group_id=group_id,
442 526
         status=True,
@@ -455,27 +539,30 @@ def flyimg_upload_api(request):
455 539
 
456 540
 
457 541
 def comment_submit_api(request):
542
+    """
543
+    飞图评论提交/飞图评论列表
544
+    :param request:
545
+    :return:
546
+    """
458 547
     group_id = request.POST.get('group_id', '')
459 548
     user_id = request.POST.get('user_id', '')
460 549
     photo_id = request.POST.get('photo_id', '')
461 550
     comment = request.POST.get('comment', '')
462 551
 
463
-    try:
464
-        group = GroupInfo.objects.get(group_id=group_id)
465
-    except GroupInfo.DoesNotExist:
466
-        return response(GroupStatusCode.GROUP_NOT_FOUND)
467
-
552
+    # 群组用户校验
468 553
     try:
469 554
         group_user = GroupUserInfo.objects.get(group_id=group_id, user_id=user_id, user_status=GroupUserInfo.PASSED)
470 555
     except GroupUserInfo.DoesNotExist:
471
-        return response(GroupStatusCode.GROUP_USER_NOT_FOUND)
556
+        return response(GroupUserStatusCode.GROUP_USER_NOT_FOUND)
472 557
 
558
+    # 群组照片校验
473 559
     try:
474 560
         group_photo = GroupPhotoInfo.objects.get(pk=photo_id)
475 561
     except GroupPhotoInfo.DoesNotExist:
476
-        return response(GroupPhotoStatusCode.MESSAGE_NOT_FOUND)
562
+        return response(GroupPhotoStatusCode.GROUP_PHOTO_NOT_FOUND)
477 563
 
478 564
     if comment:
565
+        # 群组照片评论记录创建
479 566
         PhotoCommentInfo.objects.create(
480 567
             photo_id=photo_id,
481 568
             user_id=user_id,
@@ -484,21 +571,25 @@ def comment_submit_api(request):
484 571
             comment=comment,
485 572
         )
486 573
 
574
+        # 群组照片评论数更新
487 575
         group_photo.comment_num += 1
488 576
         group_photo.save()
489 577
 
490
-        UserMessageInfo.objects.create(
491
-            from_uid=user_id,
492
-            from_nickname=group_user.nickname,
493
-            from_avatar=group_user.avatar,
494
-            to_uid=group_photo.user_id,
495
-            group_id=group_photo.group_id,
496
-            photo_id=group_photo.pk,
497
-            msg_type=UserMessageInfo.COMMENT,
498
-            msg_title=u'评论',
499
-            msg_content=comment,
500
-        )
501
-
578
+        # 判断群组照片发布者是否已经被管理员移除,如若移除,则不给发布者提醒
579
+        if r.sismember(GROUP_USERS_PASSED_SET % group_photo.group_id, group_photo.user_id):
580
+            UserMessageInfo.objects.create(
581
+                from_uid=user_id,
582
+                from_nickname=group_user.nickname,
583
+                from_avatar=group_user.avatar,
584
+                to_uid=group_photo.user_id,
585
+                group_id=group_photo.group_id,
586
+                photo_id=group_photo.pk,
587
+                msg_type=UserMessageInfo.COMMENT,
588
+                msg_title=u'评论',
589
+                msg_content=comment,
590
+            )
591
+
592
+    # 群组照片评论列表
502 593
     photo_comments = PhotoCommentInfo.objects.filter(
503 594
         photo_id=photo_id,
504 595
     )
@@ -513,25 +604,28 @@ def comment_submit_api(request):
513 604
 
514 605
 
515 606
 def thumbup_submit_api(request):
607
+    """
608
+    飞图点赞提交
609
+    :param request:
610
+    :return:
611
+    """
516 612
     group_id = request.POST.get('group_id', '')
517 613
     user_id = request.POST.get('user_id', '')
518 614
     photo_id = request.POST.get('photo_id', '')
519 615
 
520
-    try:
521
-        group = GroupInfo.objects.get(group_id=group_id)
522
-    except GroupInfo.DoesNotExist:
523
-        return response(GroupStatusCode.GROUP_NOT_FOUND)
524
-
616
+    # 群组用户校验
525 617
     try:
526 618
         group_user = GroupUserInfo.objects.get(group_id=group_id, user_id=user_id, user_status=GroupUserInfo.PASSED)
527 619
     except GroupUserInfo.DoesNotExist:
528
-        return response(GroupStatusCode.GROUP_USER_NOT_FOUND)
620
+        return response(GroupUserStatusCode.GROUP_USER_NOT_FOUND)
529 621
 
622
+    # 群组照片校验
530 623
     try:
531 624
         group_photo = GroupPhotoInfo.objects.get(pk=photo_id)
532 625
     except GroupPhotoInfo.DoesNotExist:
533
-        return response(GroupPhotoStatusCode.MESSAGE_NOT_FOUND)
626
+        return response(GroupPhotoStatusCode.GROUP_PHOTO_NOT_FOUND)
534 627
 
628
+    # 群组照片点赞记录创建/更新
535 629
     photo_thumbup, created = PhotoThumbUpInfo.objects.get_or_create(
536 630
         photo_id=photo_id,
537 631
         user_id=user_id,
@@ -541,21 +635,25 @@ def thumbup_submit_api(request):
541 635
     photo_thumbup.thumbup = True
542 636
     photo_thumbup.save()
543 637
 
638
+    # 群组照片点赞数更新
544 639
     group_photo.thumbup_num += 1
545 640
     group_photo.save()
546 641
 
547
-    UserMessageInfo.objects.create(
548
-        from_uid=user_id,
549
-        from_nickname=group_user.nickname,
550
-        from_avatar=group_user.avatar,
551
-        to_uid=group_photo.user_id,
552
-        group_id=group_photo.group_id,
553
-        photo_id=group_photo.pk,
554
-        msg_type=UserMessageInfo.THUMBUP,
555
-        msg_title=u'点赞',
556
-        msg_content=u'点赞',
557
-    )
642
+    # 判断群组照片发布者是否已经被管理员移除,如若移除,则不给发布者提醒
643
+    if r.sismember(GROUP_USERS_PASSED_SET % group_photo.group_id, group_photo.user_id):
644
+        UserMessageInfo.objects.create(
645
+            from_uid=user_id,
646
+            from_nickname=group_user.nickname,
647
+            from_avatar=group_user.avatar,
648
+            to_uid=group_photo.user_id,
649
+            group_id=group_photo.group_id,
650
+            photo_id=group_photo.pk,
651
+            msg_type=UserMessageInfo.THUMBUP,
652
+            msg_title=u'点赞',
653
+            msg_content=u'点赞',
654
+        )
558 655
 
656
+    # 群组照片点赞列表
559 657
     photo_thumbups = PhotoThumbUpInfo.objects.filter(
560 658
         photo_id=photo_id,
561 659
         thumbup=True,
@@ -572,33 +670,22 @@ def thumbup_submit_api(request):
572 670
 
573 671
 
574 672
 def thumbup_list_api(request):
673
+    """
674
+    飞图点赞列表
675
+    :param request:
676
+    :return:
677
+    """
575 678
     group_id = request.POST.get('group_id', '')
576 679
     user_id = request.POST.get('user_id', '')
577 680
     photo_id = request.POST.get('photo_id', '')
578 681
 
579
-    try:
580
-        group = GroupInfo.objects.get(group_id=group_id)
581
-    except GroupInfo.DoesNotExist:
582
-        return response(GroupStatusCode.GROUP_NOT_FOUND)
583
-
584
-    try:
585
-        group_user = GroupUserInfo.objects.get(group_id=group_id, user_id=user_id, user_status=GroupUserInfo.PASSED)
586
-    except GroupUserInfo.DoesNotExist:
587
-        return response(GroupStatusCode.GROUP_USER_NOT_FOUND)
588
-
589
-    try:
590
-        group_photo = GroupPhotoInfo.objects.get(pk=photo_id)
591
-    except GroupPhotoInfo.DoesNotExist:
592
-        return response(GroupPhotoStatusCode.MESSAGE_NOT_FOUND)
593
-
594
-    try:
595
-        thumbup = PhotoThumbUpInfo.objects.get(
596
-            photo_id=photo_id,
597
-            user_id=user_id,
598
-        ).thumbup
599
-    except PhotoThumbUpInfo.DoesNotExist:
600
-        thumbup = False
682
+    # user_id 是否点赞 photo_id
683
+    thumbup = PhotoThumbUpInfo.objects.filter(
684
+        photo_id=photo_id,
685
+        user_id=user_id,
686
+    ).exists()
601 687
 
688
+    # 群组照片点赞列表
602 689
     photo_thumbups = PhotoThumbUpInfo.objects.filter(
603 690
         photo_id=photo_id,
604 691
         thumbup=True,
@@ -615,25 +702,28 @@ def thumbup_list_api(request):
615 702
 
616 703
 
617 704
 def thumbup_cancel_api(request):
705
+    """
706
+    飞图点赞取消
707
+    :param request:
708
+    :return:
709
+    """
618 710
     group_id = request.POST.get('group_id', '')
619 711
     user_id = request.POST.get('user_id', '')
620 712
     photo_id = request.POST.get('photo_id', '')
621 713
 
622
-    try:
623
-        group = GroupInfo.objects.get(group_id=group_id)
624
-    except GroupInfo.DoesNotExist:
625
-        return response(GroupStatusCode.GROUP_NOT_FOUND)
626
-
714
+    # 群组用户校验
627 715
     try:
628 716
         group_user = GroupUserInfo.objects.get(group_id=group_id, user_id=user_id, user_status=GroupUserInfo.PASSED)
629 717
     except GroupUserInfo.DoesNotExist:
630
-        return response(GroupStatusCode.GROUP_USER_NOT_FOUND)
718
+        return response(GroupUserStatusCode.GROUP_USER_NOT_FOUND)
631 719
 
720
+    # 群组照片校验
632 721
     try:
633 722
         group_photo = GroupPhotoInfo.objects.get(pk=photo_id)
634 723
     except GroupPhotoInfo.DoesNotExist:
635
-        return response(GroupPhotoStatusCode.MESSAGE_NOT_FOUND)
724
+        return response(GroupPhotoStatusCode.GROUP_PHOTO_NOT_FOUND)
636 725
 
726
+    # 群组照片点赞取消
637 727
     photo_thumbup, created = PhotoThumbUpInfo.objects.get_or_create(
638 728
         photo_id=photo_id,
639 729
         user_id=user_id,
@@ -641,21 +731,25 @@ def thumbup_cancel_api(request):
641 731
     photo_thumbup.thumbup = False
642 732
     photo_thumbup.save()
643 733
 
734
+    # 群组照片点赞数更新
644 735
     group_photo.thumbup_num -= 1
645 736
     group_photo.save()
646 737
 
647
-    UserMessageInfo.objects.create(
648
-        from_uid=user_id,
649
-        from_nickname=group_user.nickname,
650
-        from_avatar=group_user.avatar,
651
-        to_uid=group_photo.user_id,
652
-        group_id=group_photo.group_id,
653
-        photo_id=group_photo.pk,
654
-        msg_type=UserMessageInfo.THUMBUP,
655
-        msg_title=u'取消点赞',
656
-        msg_content=u'取消点赞',
657
-    )
738
+    # 判断群组照片发布者是否已经被管理员移除,如若移除,则不给发布者提醒
739
+    if r.sismember(GROUP_USERS_PASSED_SET % group_photo.group_id, group_photo.user_id):
740
+        UserMessageInfo.objects.create(
741
+            from_uid=user_id,
742
+            from_nickname=group_user.nickname,
743
+            from_avatar=group_user.avatar,
744
+            to_uid=group_photo.user_id,
745
+            group_id=group_photo.group_id,
746
+            photo_id=group_photo.pk,
747
+            msg_type=UserMessageInfo.THUMBUP,
748
+            msg_title=u'取消点赞',
749
+            msg_content=u'取消点赞',
750
+        )
658 751
 
752
+    # 群组照片点赞列表
659 753
     photo_thumbups = PhotoThumbUpInfo.objects.filter(
660 754
         photo_id=photo_id,
661 755
         thumbup=True,
@@ -672,10 +766,16 @@ def thumbup_cancel_api(request):
672 766
 
673 767
 
674 768
 def pai2_home_api(request):
769
+    """
770
+    首页信息
771
+    :param request:
772
+    :return:
773
+    """
675 774
     user_id = request.POST.get('user_id', '')
676 775
     page = int(request.POST.get('page', 1))
677 776
     num = int(request.POST.get('num', settings.PAI2_HOME_PER_PAGE))
678 777
 
778
+    # 执行原生 SQL 语句,获取首页照片列表
679 779
     cursor = connection.cursor()
680 780
     cursor.execute(PAI2_HOME_API.format(
681 781
         user_id=user_id,
@@ -684,8 +784,10 @@ def pai2_home_api(request):
684 784
     ))
685 785
     rows = cursor.fetchall()
686 786
 
787
+    # 首页照片分页
687 788
     rows, left = pagination(rows, page, num)
688 789
 
790
+    # 首页照片信息
689 791
     rows = [{
690 792
         'group_id': row[0],
691 793
         'group_name': row[1],

+ 16 - 0
message/views.py

@@ -12,6 +12,11 @@ from utils.error.response_utils import response
12 12
 
13 13
 
14 14
 def message_list_api(request):
15
+    """
16
+    消息列表
17
+    :param request:
18
+    :return:
19
+    """
15 20
     user_id = request.POST.get('user_id', '') or request.GET.get('user_id', '')
16 21
 
17 22
     messages = UserMessageInfo.MESSAGE_TYPE_INFO
@@ -38,6 +43,12 @@ def message_list_api(request):
38 43
 
39 44
 
40 45
 def message_type_list_api(request, msg_type):
46
+    """
47
+    分类消息列表
48
+    :param request:
49
+    :param msg_type:
50
+    :return:
51
+    """
41 52
     user_id = request.POST.get('user_id', '') or request.GET.get('user_id', '')
42 53
     page = int(request.POST.get('page', 0) or request.GET.get('page', 0) or 1)
43 54
     num = int(request.POST.get('num', 0) or request.GET.get('num', 0) or settings.MESSAGE_NUM_PER_PAGE)
@@ -62,6 +73,11 @@ def message_type_list_api(request, msg_type):
62 73
 
63 74
 
64 75
 def message_read_api(request):
76
+    """
77
+    消息读取
78
+    :param request:
79
+    :return:
80
+    """
65 81
     pk = int(request.POST.get('pk', 0) or request.GET.get('pk', 0) or -1)
66 82
     user_id = request.POST.get('user_id', '') or request.GET.get('user_id', '')
67 83
 

+ 10 - 0
operation/views.py

@@ -6,6 +6,11 @@ from operation.models import LatestAppInfo, SplashInfo
6 6
 
7 7
 
8 8
 def upgrade_api(request):
9
+    """
10
+    APP 升级
11
+    :param request:
12
+    :return:
13
+    """
9 14
     try:
10 15
         appinfo = LatestAppInfo.objects.all()[0].data
11 16
     except IndexError:
@@ -21,6 +26,11 @@ def upgrade_api(request):
21 26
 
22 27
 
23 28
 def splash_api(request):
29
+    """
30
+    启动页面
31
+    :param request:
32
+    :return:
33
+    """
24 34
     splashes = SplashInfo.objects.all()
25 35
     splashes = [splash.data for splash in splashes]
26 36
 

+ 10 - 0
pay/views.py

@@ -22,6 +22,11 @@ wxpay = WeChatPay(WECHAT['appID'], WECHAT['apiKey'], WECHAT['mchID'])
22 22
 
23 23
 @transaction.atomic
24 24
 def wx_order_create_api(request):
25
+    """
26
+    订单创建
27
+    :param request:
28
+    :return:
29
+    """
25 30
     from_uid = request.POST.get('from_uid', '')
26 31
     to_lid = request.POST.get('to_lid', '')
27 32
     to_uid = request.POST.get('to_uid', '')
@@ -71,6 +76,11 @@ def order_paid_success(order):
71 76
 
72 77
 @transaction.atomic
73 78
 def wx_notify_url_api(request):
79
+    """
80
+    支付异步通知回调地址
81
+    :param request:
82
+    :return:
83
+    """
74 84
     try:
75 85
         data = xmltodict.parse(request.body)['xml']
76 86
     except xmltodict.ParsingInterrupted:

+ 30 - 4
photo/views.py

@@ -19,6 +19,9 @@ from utils.watermark_utils import watermark_wrap
19 19
 from utils.error.errno_utils import PhotoStatusCode
20 20
 from utils.error.response_utils import response
21 21
 
22
+from utils.redis.rkeys import GROUP_LAST_PHOTO_PK
23
+from utils.redis.rgroup import get_group_info, get_group_users_info
24
+
22 25
 from curtail_uuid import CurtailUUID
23 26
 
24 27
 from TimeConvert import TimeConvert as tc
@@ -27,7 +30,15 @@ import os
27 30
 import shortuuid
28 31
 
29 32
 
33
+r = settings.REDIS_CACHE
34
+
35
+
30 36
 def uuid_init(request):
37
+    """
38
+    生成唯一标识
39
+    :param request:
40
+    :return:
41
+    """
31 42
     num = int(request.GET.get('num', 1000))
32 43
 
33 44
     for i in xrange(num):
@@ -43,6 +54,11 @@ def uuid_init(request):
43 54
 # curl -X POST -F user=xxxxxxx -F num=100 http://api.pai.ai/uuid
44 55
 @transaction.atomic
45 56
 def uuid(request):
57
+    """
58
+    获取唯一标识
59
+    :param request:
60
+    :return:
61
+    """
46 62
     lensman_id = request.POST.get('user', '')
47 63
     num = int(request.POST.get('num', 100))
48 64
 
@@ -72,6 +88,11 @@ def uuid(request):
72 88
 #
73 89
 # curl -X POST -F user=xxxxxxx -F session=xxxxxxx -F photo_id=xxxxxxx -F photo=@xxxxxxx.jpg http://api.pai.ai/photos/upload
74 90
 def upload_photo(request):
91
+    """
92
+    上传图片
93
+    :param request:
94
+    :return:
95
+    """
75 96
     lensman_id = request.POST.get('user', '')
76 97
     session_id = request.POST.get('session', '')
77 98
     photo_id = request.POST.get('photo_id', '')
@@ -121,6 +142,12 @@ def upload_photo(request):
121 142
 
122 143
 
123 144
 def session_detail_api(request, session):
145
+    """
146
+    Session 详情
147
+    :param request:
148
+    :param session:
149
+    :return:
150
+    """
124 151
     user_id = request.POST.get('user_id', '')
125 152
 
126 153
     # 判断 user_id 是否存在,如果不存在,则直接分配帐户
@@ -142,9 +169,8 @@ def session_detail_api(request, session):
142 169
     group_id = group.group_id
143 170
 
144 171
     # 判断 group_id/user_id 的群组用户是否存在,如果不存在,则直接创建
145
-    group_photo = GroupPhotoInfo.objects.filter(group_id=group_id).last()
146 172
     group_user = GroupUserInfo.objects.get_or_create(group_id=group_id, user_id=user_id, defaults={
147
-        'current_id': group_photo and group_photo.id or -1,
173
+        'current_id': int(r.get(GROUP_LAST_PHOTO_PK % group_id) or -1),
148 174
         'nickname': user.final_nickname,
149 175
         'admin': False,
150 176
         'user_status': GroupUserInfo.PASSED,
@@ -159,9 +185,9 @@ def session_detail_api(request, session):
159 185
             'count': photos.count(),
160 186
             'photos': [p.detail for p in photos],
161 187
             'group_id': group_id,
162
-            'group': group.data,
188
+            'group': get_group_info(group_id),
163 189
             'user_id': user_id,
164
-            'users': group.users(user_id),
190
+            'users': get_group_users_info(group_id, user_id),
165 191
         }
166 192
     })
167 193
 

+ 0 - 1
utils/error/errno_utils.py

@@ -45,7 +45,6 @@ class GroupStatusCode(BaseStatusCode):
45 45
     NO_REFUSE_PERMISSION = StatusCodeField(402009, u'No Refuse Permission', description=u'没有拒绝权限')
46 46
     DUPLICATE_JOIN_REQUEST = StatusCodeField(402010, u'Duplicate Join Request', description=u'重复加群申请')
47 47
     JOIN_REQUEST_NOT_FOUND = StatusCodeField(402011, u'Join Request Not Found', description=u'加群申请不存在')
48
-    GROUP_USER_NOT_FOUND = StatusCodeField(402012, u'Group User Not Found', description=u'该用户不在群组')
49 48
 
50 49
 
51 50
 class GroupUserStatusCode(BaseStatusCode):

+ 2 - 1
utils/error/response_utils.py

@@ -6,6 +6,7 @@ from django.http import JsonResponse
6 6
 def response(status_code, data={}):
7 7
     return JsonResponse({
8 8
         'status': status_code,
9
-        'message': status_code.description,
9
+        'message': status_code.message,
10
+        'description': status_code.description,
10 11
         'data': data,
11 12
     })

+ 0 - 7
utils/redis/keys.py

@@ -1,7 +0,0 @@
1
-# -*- coding: utf-8 -*-
2
-
3
-# 用户相关
4
-PROFILE_USER_INFO = 'profile:user:info:%s'  # 用户信息,uid
5
-
6
-# 群组相关
7
-GROUP_INFO = 'group:info:%s'  # 群组信息,group_id

+ 33 - 0
utils/redis/retrieve.py

@@ -0,0 +1,33 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf import settings
4
+
5
+from group.models import GroupInfo, GroupUserInfo, GroupPhotoInfo
6
+
7
+from utils.redis.rkeys import GROUP_USERS_APPLYING_SET, GROUP_USERS_PASSED_SET, GROUP_USERS_REFUSED_SET, GROUP_USERS_DELETED_SET
8
+from utils.redis.rkeys import GROUP_LAST_PHOTO_PK
9
+
10
+
11
+r = settings.REDIS_CACHE
12
+
13
+
14
+def retrieve_redis_data():
15
+    # 群组/群组照片相关
16
+    groups = GroupInfo.objects.filter(status=True)
17
+    for group in groups:
18
+        group_photo = GroupPhotoInfo.objects.filter(group_id=group.group_id).last()
19
+        # 设置群组最后一张照片PK
20
+        r.set(GROUP_LAST_PHOTO_PK % group.group_id, group_photo and group_photo.pk or -1)
21
+
22
+    # 群组用户相关
23
+    group_users = GroupUserInfo.objects.filter(status=True)
24
+    for group_user in group_users:
25
+        group_id, user_id = group_user.group_id, group_user.user_id
26
+        if group_user.user_status == GroupUserInfo.PASSED:
27
+            r.sadd(GROUP_USERS_PASSED_SET % group_id, user_id)
28
+        elif group_user.user_status == GroupUserInfo.DELETED:
29
+            r.sadd(GROUP_USERS_DELETED_SET % group_id, user_id)
30
+        elif group_user.user_status == GroupUserInfo.APPLYING:
31
+            r.sadd(GROUP_USERS_APPLYING_SET % group_id, user_id)
32
+        elif group_user.user_status == GroupUserInfo.REFUSED:
33
+            r.sadd(GROUP_USERS_REFUSED_SET % group_id, user_id)

+ 68 - 0
utils/redis/rgroup.py

@@ -0,0 +1,68 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf import settings
4
+from django.core.serializers.json import DjangoJSONEncoder
5
+
6
+from utils.redis.rkeys import GROUP_INFO, GROUP_USERS_INFO, GROUP_LAST_PHOTO_PK
7
+
8
+import json
9
+
10
+
11
+r = settings.REDIS_CACHE
12
+
13
+
14
+# 群组信息相关
15
+
16
+
17
+def set_group_info(group):
18
+    """ 设置群组信息 """
19
+    group_data = group.data
20
+    r.set(GROUP_INFO % group.group_id, json.dumps(group_data, cls=DjangoJSONEncoder))
21
+    return group_data
22
+
23
+
24
+def set_group_info_by_id(group_id):
25
+    """ 设置群组信息 """
26
+    from group.models import GroupInfo
27
+    try:
28
+        group = GroupInfo.objects.get(group_id=group_id)
29
+    except GroupInfo.DoesNotExist:
30
+        return {}
31
+    return set_group_info(group)
32
+
33
+
34
+def get_group_info(group_id):
35
+    """ 获取群组信息 """
36
+    return json.loads(r.get(GROUP_INFO % group_id) or '{}') or set_group_info_by_id(group_id)
37
+
38
+
39
+# 群组用户信息相关
40
+
41
+
42
+def set_group_users_info(group):
43
+    """ 设置群组用户信息 """
44
+    group_users = group.users(admin=False)
45
+    r.set(GROUP_USERS_INFO % group.group_id, json.dumps({
46
+        'group_users': group_users,
47
+        'admin_id': group.admin_id,
48
+    }, cls=DjangoJSONEncoder))
49
+    return group_users
50
+
51
+
52
+def set_group_users_info_by_id(group_id):
53
+    """ 设置群组用户信息 """
54
+    from group.models import GroupInfo
55
+    try:
56
+        group = GroupInfo.objects.get(group_id=group_id)
57
+    except GroupInfo.DoesNotExist:
58
+        return {}
59
+    return set_group_users_info(group)
60
+
61
+
62
+def get_group_users_info(group_id, user_id):
63
+    """ 获取群组用户信息 """
64
+    group_users_data = json.loads(r.get(GROUP_USERS_INFO % group_id) or '{}') or set_group_users_info_by_id(group_id)
65
+    group_users, admin_id = group_users_data.get('group_users', {}), group_users_data.get('admin_id', '')
66
+    if group_users and user_id != admin_id:
67
+        [group_users.pop(k) for k in ['applying_count', 'applying']]
68
+    return group_users

+ 17 - 0
utils/redis/rkeys.py

@@ -0,0 +1,17 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+# 用户相关
4
+PROFILE_INFO = 'profile:info:%s'  # STRING,用户信息,uid
5
+
6
+# 群组相关
7
+GROUP_INFO = 'group:info:%s'  # STRING,群组信息,group_id
8
+
9
+# 群组用户相关
10
+GROUP_USERS_INFO = 'group:users:info:%s'  # STRING,群组用户信息,group_id
11
+GROUP_USERS_APPLYING_SET = 'group:users:applying:set:%s'  # SET,群组用户申请集合,group_id
12
+GROUP_USERS_PASSED_SET = 'group:users:passed:set:%s'  # SET,群组用户通过集合,group_id
13
+GROUP_USERS_REFUSED_SET = 'group:users:refused:set:%s'  # SET,群组用户拒绝集合,group_id
14
+GROUP_USERS_DELETED_SET = 'group:users:deleted:set:%s'  # SET,群组用户移除集合,group_id
15
+
16
+# 群组照片相关
17
+GROUP_LAST_PHOTO_PK = 'group:last:photo:pk:%s'  # STRING,群组最后一张照片PK,group_id