shop.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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. // 百度地图达到上限后会导致加车失败,需优化
  275. console.log('从addressComoponent获取测距数据失败', err);
  276. restaurants.map((item, index) => {
  277. return Object.assign(item, {distance: '10公里', order_lead_time: '40分钟'})
  278. })
  279. }
  280. try{
  281. res.send(restaurants)
  282. }catch(err){
  283. res.send({
  284. status: 0,
  285. type: 'ERROR_GET_SHOP_LIST',
  286. message: '获取店铺列表数据失败'
  287. })
  288. }
  289. }
  290. //搜索餐馆
  291. async searchResaturant(req, res, next){
  292. const {geohash, keyword} = req.query;
  293. try{
  294. if (!geohash || geohash.indexOf(',') == -1) {
  295. throw new Error('经纬度参数错误');
  296. }else if(!keyword){
  297. throw new Error('关键词参数错误');
  298. }
  299. }catch(err){
  300. console.log('搜索商铺参数错误');
  301. res.send({
  302. status: 0,
  303. type: 'ERROR_PARAMS',
  304. message: err.message,
  305. })
  306. return
  307. }
  308. try{
  309. const restaurants = await ShopModel.find({name: eval('/' + keyword + '/gi')}, '-_id').limit(50);
  310. if (restaurants.length) {
  311. const [latitude, longitude] = geohash.split(',');
  312. const from = latitude + ',' + longitude;
  313. let to = '';
  314. //获取百度地图测局所需经度纬度
  315. restaurants.forEach((item, index) => {
  316. const slpitStr = (index == restaurants.length -1) ? '' : '|';
  317. to += item.latitude + ',' + item.longitude + slpitStr;
  318. })
  319. //获取距离信息,并合并到数据中
  320. const distance_duration = await this.getDistance(from, to)
  321. restaurants.map((item, index) => {
  322. return Object.assign(item, distance_duration[index])
  323. })
  324. }
  325. res.send(restaurants);
  326. }catch(err){
  327. console.log('搜索餐馆数据失败');
  328. res.send({
  329. status: 0,
  330. type: 'ERROR_DATA',
  331. message: '搜索餐馆数据失败'
  332. })
  333. }
  334. }
  335. //获取餐馆详情
  336. async getRestaurantDetail(req, res, next){
  337. const restaurant_id = req.params.restaurant_id;
  338. if (!restaurant_id || !Number(restaurant_id)) {
  339. console.log('获取餐馆详情参数ID错误');
  340. res.send({
  341. status: 0,
  342. type: 'ERROR_PARAMS',
  343. message: '餐馆ID参数错误',
  344. })
  345. return
  346. }
  347. try{
  348. const restaurant = await ShopModel.findOne({id: restaurant_id}, '-_id');
  349. res.send(restaurant)
  350. }catch(err){
  351. console.log('获取餐馆详情失败', err);
  352. res.send({
  353. status: 0,
  354. type: 'GET_DATA_ERROR',
  355. message: '获取餐馆详情失败'
  356. })
  357. }
  358. }
  359. async getShopCount(req, res, next){
  360. try{
  361. const count = await ShopModel.count();
  362. res.send({
  363. status: 1,
  364. count,
  365. })
  366. }catch(err){
  367. console.log('获取餐馆数量失败', err);
  368. res.send({
  369. status: 0,
  370. type: 'ERROR_TO_GET_COUNT',
  371. message: '获取餐馆数量失败'
  372. })
  373. }
  374. }
  375. async updateshop(req, res, next){
  376. const form = new formidable.IncomingForm();
  377. form.parse(req, async (err, fields, files) => {
  378. if (err) {
  379. console.log('获取商铺信息form出错', err);
  380. res.send({
  381. status: 0,
  382. type: 'ERROR_FORM',
  383. message: '表单信息错误',
  384. })
  385. return
  386. }
  387. const {name, address, description = "", phone, category, id, latitude, longitude, image_path} = fields;
  388. if (id == 1) {
  389. res.send({
  390. status: 0,
  391. message: '此店铺用做展示,请不要修改'
  392. })
  393. return
  394. }
  395. try{
  396. if (!name) {
  397. throw new Error('餐馆名称错误');
  398. }else if(!address){
  399. throw new Error('餐馆地址错误');
  400. }else if(!phone){
  401. throw new Error('餐馆联系电话错误');
  402. }else if(!category){
  403. throw new Error('餐馆分类错误');
  404. }else if(!id || !Number(id)){
  405. throw new Error('餐馆ID错误');
  406. }else if(!image_path){
  407. throw new Error('餐馆图片地址错误');
  408. }
  409. let newData;
  410. if (latitude && longitude) {
  411. newData = {name, address, description, phone, category, latitude, longitude, image_path}
  412. }else{
  413. newData = {name, address, description, phone, category, image_path}
  414. }
  415. await ShopModel.findOneAndUpdate({id}, {$set: newData});
  416. res.send({
  417. status: 1,
  418. success: '修改商铺信息成功',
  419. })
  420. }catch(err){
  421. console.log(err.message, err);
  422. res.send({
  423. status: 0,
  424. type: 'ERROR_UPDATE_RESTAURANT',
  425. message: '更新商铺信息失败',
  426. })
  427. }
  428. })
  429. }
  430. async deleteResturant(req, res, next){
  431. const restaurant_id = req.params.restaurant_id;
  432. if (!restaurant_id || !Number(restaurant_id)) {
  433. console.log('restaurant_id参数错误');
  434. res.send({
  435. status: 0,
  436. type: 'ERROR_PARAMS',
  437. message: 'restaurant_id参数错误',
  438. })
  439. return
  440. }
  441. if (restaurant_id == 1) {
  442. res.send({
  443. status: 0,
  444. message: '此店铺用做展示,请不要删除'
  445. })
  446. return
  447. }
  448. try{
  449. await ShopModel.remove({id: restaurant_id});
  450. res.send({
  451. status: 1,
  452. success: '删除餐馆成功',
  453. })
  454. }catch(err){
  455. console.log('删除餐馆失败', err);
  456. res.send({
  457. status: 0,
  458. type: 'DELETE_RESTURANT_FAILED',
  459. message: '删除餐馆失败',
  460. })
  461. }
  462. }
  463. }
  464. export default new Shop()