shop.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. 'use strict';
  2. import ShopModel from '../../models/shopping/shop'
  3. import AddressComponent from '../../prototype/addressComponent'
  4. import Food from './food'
  5. import formidable from 'formidable'
  6. import CategoryHandle from './category'
  7. import Rating from '../ugc/rating'
  8. class Shop extends AddressComponent{
  9. constructor(){
  10. super()
  11. this.addShop = this.addShop.bind(this);
  12. this.getRestaurants = this.getRestaurants.bind(this);
  13. this.searchResaturant = this.searchResaturant.bind(this);
  14. }
  15. //添加商铺
  16. async addShop(req, res, next){
  17. let restaurant_id;
  18. try{
  19. restaurant_id = await this.getId('restaurant_id');
  20. }catch(err){
  21. console.log('获取商店id失败');
  22. res.send({
  23. type: 'ERROR_DATA',
  24. message: '获取数据失败'
  25. })
  26. return
  27. }
  28. const form = new formidable.IncomingForm();
  29. form.parse(req, async (err, fields, files) => {
  30. try{
  31. if (!fields.name) {
  32. throw new Error('必须填写商店名称');
  33. }else if(!fields.address){
  34. throw new Error('必须填写商店地址');
  35. }else if(!fields.phone){
  36. throw new Error('必须填写联系电话');
  37. }else if(!fields.latitude || !fields.longitude){
  38. throw new Error('商店位置信息错误');
  39. }else if(!fields.image_path){
  40. throw new Error('必须上传商铺图片');
  41. }else if(!fields.category){
  42. throw new Error('必须上传食品种类');
  43. }
  44. }catch(err){
  45. console.log('前台参数出错', err.message);
  46. res.send({
  47. status: 0,
  48. type: 'ERROR_PARAMS',
  49. message: err.message
  50. })
  51. return
  52. }
  53. const opening_hours = fields.startTime&&fields.endTime? fields.startTime + '/' + fields.endTime : "8:30/20:30";
  54. const newShop = {
  55. name: fields.name,
  56. address: fields.address,
  57. description: fields.description || '',
  58. float_delivery_fee: fields.float_delivery_fee || 0,
  59. float_minimum_order_amount: fields.float_minimum_order_amount || 0,
  60. id: restaurant_id,
  61. is_premium: fields.is_premium || false,
  62. is_new: fields.new || false,
  63. latitude: fields.latitude,
  64. longitude: fields.longitude,
  65. location: [fields.longitude, fields.latitude],
  66. opening_hours: [opening_hours],
  67. phone: fields.phone,
  68. promotion_info: fields.promotion_info || "欢迎光临,用餐高峰请提前下单,谢谢",
  69. rating: (4 + Math.random()).toFixed(1),
  70. rating_count: Math.ceil(Math.random()*1000),
  71. recent_order_num: Math.ceil(Math.random()*1000),
  72. status: Math.round(Math.random()),
  73. image_path: fields.image_path,
  74. category: fields.category,
  75. piecewise_agent_fee: {
  76. tips: "配送费约¥" + (fields.float_delivery_fee || 0),
  77. },
  78. activities: [],
  79. supports: [],
  80. license: {
  81. business_license_image: fields.business_license_image || '',
  82. catering_service_license_image: fields.catering_service_license_image || '',
  83. },
  84. identification: {
  85. company_name: "",
  86. identificate_agency: "",
  87. identificate_date: "",
  88. legal_person: "",
  89. licenses_date: "",
  90. licenses_number: "",
  91. licenses_scope: "",
  92. operation_period: "",
  93. registered_address: "",
  94. registered_number: "",
  95. },
  96. }
  97. //配送方式
  98. if (fields.delivery_mode) {
  99. Object.assign(newShop, {delivery_mode: {
  100. color: "57A9FF",
  101. id: 1,
  102. is_solid: true,
  103. text: "蜂鸟专送"
  104. }})
  105. }
  106. //商店支持的活动
  107. fields.activities.forEach((item, index) => {
  108. switch(item.icon_name){
  109. case '减':
  110. item.icon_color = 'f07373';
  111. item.id = index + 1;
  112. break;
  113. case '特':
  114. item.icon_color = 'EDC123';
  115. item.id = index + 1;
  116. break;
  117. case '新':
  118. item.icon_color = '70bc46';
  119. item.id = index + 1;
  120. break;
  121. case '领':
  122. item.icon_color = 'E3EE0D';
  123. item.id = index + 1;
  124. break;
  125. }
  126. newShop.activities.push(item);
  127. })
  128. if (fields.bao) {
  129. newShop.supports.push({
  130. description: "已加入“外卖保”计划,食品安全有保障",
  131. icon_color: "999999",
  132. icon_name: "保",
  133. id: 7,
  134. name: "外卖保"
  135. })
  136. }
  137. if (fields.zhun) {
  138. newShop.supports.push({
  139. description: "准时必达,超时秒赔",
  140. icon_color: "57A9FF",
  141. icon_name: "准",
  142. id: 9,
  143. name: "准时达"
  144. })
  145. }
  146. if (fields.piao) {
  147. newShop.supports.push({
  148. description: "该商家支持开发票,请在下单时填写好发票抬头",
  149. icon_color: "999999",
  150. icon_name: "票",
  151. id: 4,
  152. name: "开发票"
  153. })
  154. }
  155. try{
  156. //保存数据,并增加对应食品种类的数量
  157. const shop = new ShopModel(newShop);
  158. await shop.save();
  159. CategoryHandle.addCategory(fields.category)
  160. Rating.initData(restaurant_id);
  161. Food.initData(restaurant_id);
  162. res.send({
  163. status: 1,
  164. sussess: '添加餐馆成功',
  165. shopDetail: newShop
  166. })
  167. }catch(err){
  168. console.log('商铺写入数据库失败');
  169. res.send({
  170. status: 0,
  171. type: 'ERROR_SERVER',
  172. message: '添加商铺失败',
  173. })
  174. }
  175. })
  176. }
  177. //获取餐馆列表
  178. async getRestaurants(req, res, next){
  179. const {
  180. latitude,
  181. longitude,
  182. offset = 0,
  183. limit = 20,
  184. keyword,
  185. restaurant_category_id,
  186. order_by,
  187. extras,
  188. delivery_mode = [],
  189. support_ids = [],
  190. restaurant_category_ids = [],
  191. } = req.query;
  192. try{
  193. if (!latitude) {
  194. throw new Error('latitude参数错误')
  195. }else if(!longitude){
  196. throw new Error('longitude参数错误');
  197. }
  198. }catch(err){
  199. console.log('latitude,longitude参数错误');
  200. res.send({
  201. status: 0,
  202. type: 'ERROR_PARAMS',
  203. message: err.message
  204. })
  205. return
  206. }
  207. let filter = {};
  208. //获取对应食品种类
  209. if (restaurant_category_ids.length && Number(restaurant_category_ids[0])) {
  210. const category = await CategoryHandle.findById(restaurant_category_ids[0]);
  211. Object.assign(filter, {category})
  212. }
  213. //按照距离,评分,销量等排序
  214. let sortBy = {};
  215. if (Number(order_by)) {
  216. switch(Number(order_by)){
  217. case 1:
  218. Object.assign(sortBy, {float_minimum_order_amount: 1});
  219. break;
  220. case 2:
  221. Object.assign(filter, {location: {$near: [longitude, latitude]}});
  222. break;
  223. case 3:
  224. Object.assign(sortBy, {rating: -1});
  225. break;
  226. case 5:
  227. Object.assign(filter, {location: {$near: [longitude, latitude]}});
  228. break;
  229. case 6:
  230. Object.assign(sortBy, {recent_order_num: -1});
  231. break;
  232. }
  233. }
  234. //查找配送方式
  235. if (delivery_mode.length) {
  236. delivery_mode.forEach(item => {
  237. if (Number(item)) {
  238. Object.assign(filter, {'delivery_mode.id': Number(item)})
  239. }
  240. })
  241. }
  242. //查找活动支持方式
  243. if (support_ids.length) {
  244. const filterArr = [];
  245. support_ids.forEach(item => {
  246. if (Number(item) && (Number(item) !== 8)) {
  247. filterArr.push(Number(item))
  248. }else if(Number(item) == 8){ //品牌保证特殊处理
  249. Object.assign(filter, {is_premium: true})
  250. }
  251. })
  252. if (filterArr.length) {
  253. //匹配同时拥有多种活动的数据
  254. Object.assign(filter, {'supports.id': {$all: filterArr}})
  255. }
  256. }
  257. const restaurants = await ShopModel.find(filter, '-_id').sort(sortBy).limit(Number(limit)).skip(Number(offset))
  258. const from = latitude + ',' + longitude;
  259. let to = '';
  260. //获取百度地图测局所需经度纬度
  261. restaurants.forEach((item, index) => {
  262. const slpitStr = (index == restaurants.length -1) ? '' : '|';
  263. to += item.latitude + ',' + item.longitude + slpitStr;
  264. })
  265. try{
  266. if (restaurants.length) {
  267. //获取距离信息,并合并到数据中
  268. const distance_duration = await this.getDistance(from, to)
  269. restaurants.map((item, index) => {
  270. return Object.assign(item, distance_duration[index])
  271. })
  272. }
  273. }catch(err){
  274. console.log('从addressComoponent获取测距数据失败', err);
  275. restaurants.map((item, index) => {
  276. return Object.assign(item, {distance: '10公里', order_lead_time: '40分钟'})
  277. })
  278. }
  279. try{
  280. res.send(restaurants)
  281. }catch(err){
  282. res.send({
  283. status: 0,
  284. type: 'ERROR_GET_SHOP_LIST',
  285. message: '获取店铺列表数据失败'
  286. })
  287. }
  288. }
  289. //搜索餐馆
  290. async searchResaturant(req, res, next){
  291. const {geohash, keyword} = req.query;
  292. try{
  293. if (!geohash || geohash.indexOf(',') == -1) {
  294. throw new Error('经纬度参数错误');
  295. }else if(!keyword){
  296. throw new Error('关键词参数错误');
  297. }
  298. }catch(err){
  299. console.log('搜索商铺参数错误');
  300. res.send({
  301. status: 0,
  302. type: 'ERROR_PARAMS',
  303. message: err.message,
  304. })
  305. return
  306. }
  307. try{
  308. const restaurants = await ShopModel.find({name: eval('/' + keyword + '/gi')}, '-_id').limit(50);
  309. if (restaurants.length) {
  310. const [latitude, longitude] = geohash.split(',');
  311. const from = latitude + ',' + longitude;
  312. let to = '';
  313. //获取百度地图测局所需经度纬度
  314. restaurants.forEach((item, index) => {
  315. const slpitStr = (index == restaurants.length -1) ? '' : '|';
  316. to += item.latitude + ',' + item.longitude + slpitStr;
  317. })
  318. //获取距离信息,并合并到数据中
  319. const distance_duration = await this.getDistance(from, to)
  320. restaurants.map((item, index) => {
  321. return Object.assign(item, distance_duration[index])
  322. })
  323. }
  324. res.send(restaurants);
  325. }catch(err){
  326. console.log('搜索餐馆数据失败');
  327. res.send({
  328. status: 0,
  329. type: 'ERROR_DATA',
  330. message: '搜索餐馆数据失败'
  331. })
  332. }
  333. }
  334. //获取餐馆详情
  335. async getRestaurantDetail(req, res, next){
  336. const restaurant_id = req.params.restaurant_id;
  337. if (!restaurant_id || !Number(restaurant_id)) {
  338. console.log('获取餐馆详情参数ID错误');
  339. res.send({
  340. status: 0,
  341. type: 'ERROR_PARAMS',
  342. message: '餐馆ID参数错误',
  343. })
  344. return
  345. }
  346. try{
  347. const restaurant = await ShopModel.findOne({id: restaurant_id}, '-_id');
  348. res.send(restaurant)
  349. }catch(err){
  350. console.log('获取餐馆详情失败', err);
  351. res.send({
  352. status: 0,
  353. type: 'GET_DATA_ERROR',
  354. message: '获取餐馆详情失败'
  355. })
  356. }
  357. }
  358. async getShopCount(req, res, next){
  359. try{
  360. const count = await ShopModel.count();
  361. res.send({
  362. status: 1,
  363. count,
  364. })
  365. }catch(err){
  366. console.log('获取餐馆数量失败', err);
  367. res.send({
  368. status: 0,
  369. type: 'ERROR_TO_GET_COUNT',
  370. message: '获取餐馆数量失败'
  371. })
  372. }
  373. }
  374. async updateshop(req, res, next){
  375. const form = new formidable.IncomingForm();
  376. form.parse(req, async (err, fields, files) => {
  377. if (err) {
  378. console.log('获取商铺信息form出错', err);
  379. res.send({
  380. status: 0,
  381. type: 'ERROR_FORM',
  382. message: '表单信息错误',
  383. })
  384. return
  385. }
  386. const {name, address, description = "", phone, category, id, latitude, longitude, image_path} = fields;
  387. if (id == 1) {
  388. res.send({
  389. status: 0,
  390. message: '此店铺用做展示,请不要修改'
  391. })
  392. return
  393. }
  394. try{
  395. if (!name) {
  396. throw new Error('餐馆名称错误');
  397. }else if(!address){
  398. throw new Error('餐馆地址错误');
  399. }else if(!phone){
  400. throw new Error('餐馆联系电话错误');
  401. }else if(!category){
  402. throw new Error('餐馆分类错误');
  403. }else if(!id || !Number(id)){
  404. throw new Error('餐馆ID错误');
  405. }else if(!image_path){
  406. throw new Error('餐馆图片地址错误');
  407. }
  408. let newData;
  409. if (latitude && longitude) {
  410. newData = {name, address, description, phone, category, latitude, longitude, image_path}
  411. }else{
  412. newData = {name, address, description, phone, category, image_path}
  413. }
  414. await ShopModel.findOneAndUpdate({id}, {$set: newData});
  415. res.send({
  416. status: 1,
  417. success: '修改商铺信息成功',
  418. })
  419. }catch(err){
  420. console.log(err.message, err);
  421. res.send({
  422. status: 0,
  423. type: 'ERROR_UPDATE_RESTAURANT',
  424. message: '更新商铺信息失败',
  425. })
  426. }
  427. })
  428. }
  429. async deleteResturant(req, res, next){
  430. const restaurant_id = req.params.restaurant_id;
  431. if (!restaurant_id || !Number(restaurant_id)) {
  432. console.log('restaurant_id参数错误');
  433. res.send({
  434. status: 0,
  435. type: 'ERROR_PARAMS',
  436. message: 'restaurant_id参数错误',
  437. })
  438. return
  439. }
  440. try{
  441. await ShopModel.remove({id: restaurant_id});
  442. res.send({
  443. status: 1,
  444. success: '删除餐馆成功',
  445. })
  446. }catch(err){
  447. console.log('删除餐馆失败', err);
  448. res.send({
  449. status: 0,
  450. type: 'DELETE_RESTURANT_FAILED',
  451. message: '删除餐馆失败',
  452. })
  453. }
  454. }
  455. }
  456. export default new Shop()