shop.js 13 KB

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