GoodService.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. var _ = require('lodash');
  2. var path = require("path");
  3. var dao = require(path.join(process.cwd(),"dao/DAO"));
  4. var goodAttributeDao = require(path.join(process.cwd(),"dao/GoodAttributeDAO"));
  5. var orm = require("orm");
  6. var Promise = require("bluebird");
  7. var fs = require("fs");
  8. var gm = require("gm");
  9. var uniqid = require('uniqid');
  10. var upload_config = require('config').get("upload_config");
  11. /**
  12. * 裁剪图片
  13. *
  14. * @param {[type]} srcPath 原始图片路径
  15. * @param {[type]} savePath 存储路径
  16. * @param {[type]} newWidth 新的宽度
  17. * @param {[type]} newHeight 新的高度
  18. * @return {[type]} [description]
  19. */
  20. function clipImage(srcPath,savePath,newWidth,newHeight) {
  21. return new Promise(function(resolve,reject) {
  22. // console.log("src => %s",srcPath);
  23. // console.log("save => %s",savePath);
  24. /*
  25. gm(srcPath)
  26. .resize(newWidth,newHeight)
  27. .autoOrient()
  28. .write(savePath,function(err){
  29. resolve();
  30. })
  31. */
  32. // 创建读取流
  33. readable = fs.createReadStream(srcPath);
  34. // 创建写入流
  35. writable = fs.createWriteStream(savePath);
  36. readable.pipe(writable);
  37. readable.on('end',function() {
  38. resolve();
  39. });
  40. });
  41. }
  42. /**
  43. * 通过参数生成产品基本信息
  44. *
  45. * @param {[type]} params.cb [description]
  46. * @return {[type]} [description]
  47. */
  48. function generateGoodInfo(params) {
  49. return new Promise(function(resolve,reject){
  50. var info = {};
  51. if(params.goods_id) info["goods_id"] = params.goods_id;
  52. if(!params.goods_name) return reject("商品名称不能为空");
  53. info["goods_name"] = params.goods_name;
  54. if(!params.goods_price) return reject("商品价格不能为空");
  55. var price = parseFloat(params.goods_price);
  56. if(isNaN(price) || price < 0) return reject("商品价格不正确")
  57. info["goods_price"] = price;
  58. if(!params.goods_number) return reject("商品数量不能为空");
  59. var num = parseInt(params.goods_number);
  60. if(isNaN(num) || num < 0) return reject("商品数量不正确");
  61. info["goods_number"] = num;
  62. if(!params.goods_cat) return reject("商品没有设置所属分类");
  63. var cats = params.goods_cat.split(',');
  64. if(cats.length > 0) {
  65. info["cat_one_id"] = cats[0];
  66. }
  67. if(cats.length > 1) {
  68. info["cat_two_id"] = cats[1];
  69. }
  70. if(cats.length > 2) {
  71. info["cat_three_id"] = cats[2];
  72. info["cat_id"] = cats[2];
  73. }
  74. if(params.goods_weight) {
  75. weight = parseFloat(params.goods_weight);
  76. if(isNaN(weight) || weight < 0) return reject("商品重量格式不正确");
  77. info["goods_weight"] = weight;
  78. } else {
  79. info["goods_weight"] = 0;
  80. }
  81. if(params.goods_introduce) {
  82. info["goods_introduce"] = params.goods_introduce;
  83. }
  84. if(params.goods_big_logo) {
  85. info["goods_big_logo"] = params.goods_big_logo;
  86. } else {
  87. info["goods_big_logo"] = "";
  88. }
  89. if(params.goods_small_logo) {
  90. info["goods_small_logo"] = params.goods_small_logo;
  91. } else {
  92. info["goods_small_logo"] = "";
  93. }
  94. if(params.goods_state) {
  95. info["goods_state"] = params.goods_state;
  96. }
  97. // 图片
  98. if(params.pics) {
  99. info["pics"] = params.pics;
  100. }
  101. // 属性
  102. if(params.attrs) {
  103. info["attrs"] = params.attrs;
  104. }
  105. info["add_time"] = Date.parse(new Date()) / 1000;
  106. info["upd_time"] = Date.parse(new Date()) / 1000;
  107. info["is_del"] = '0';
  108. if(params.hot_mumber) {
  109. hot_num = parseInt(params.hot_mumber);
  110. if(isNaN(hot_num) || hot_num < 0) return reject("热销品数量格式不正确");
  111. info["hot_mumber"] = hot_num;
  112. } else {
  113. info["hot_mumber"] = 0;
  114. }
  115. info["is_promote"] = info["is_promote"] ? info["is_promote"] : false;
  116. resolve(info);
  117. });
  118. }
  119. /**
  120. * 检查商品名称是否重复
  121. *
  122. * @param {[type]} info [description]
  123. * @return {[type]} [description]
  124. */
  125. function checkGoodName(info) {
  126. return new Promise(function(resolve,reject) {
  127. dao.findOne("GoodModel",{"goods_name":info.goods_name,"is_del":"0"},function(err,good) {
  128. if(err) return reject(err);
  129. if(!good) return resolve(info);
  130. if(good.goods_id == info.goods_id) return resolve(info);
  131. return reject("商品名称已存在");
  132. });
  133. });
  134. }
  135. /**
  136. * 创建商品基本信息
  137. *
  138. * @param {[type]} info [description]
  139. * @return {[type]} [description]
  140. */
  141. function createGoodInfo(info) {
  142. return new Promise(function(resolve,reject){
  143. dao.create("GoodModel",_.clone(info),function(err,newGood) {
  144. if(err) return reject("创建商品基本信息失败");
  145. newGood.goods_cat = newGood.getGoodsCat();
  146. info.good = newGood;
  147. return resolve(info);
  148. });
  149. });
  150. }
  151. function updateGoodInfo(info) {
  152. return new Promise(function(resolve,reject){
  153. if(!info.goods_id) return reject("商品ID不存在");
  154. dao.update("GoodModel",info.goods_id,_.clone(info),function(err,newGood) {
  155. if(err) return reject("更新商品基本信息失败");
  156. info.good = newGood;
  157. return resolve(info);
  158. });
  159. });
  160. }
  161. /**
  162. * 获取商品对象
  163. *
  164. * @param {[type]} info 查询内容
  165. * @return {[type]} [description]
  166. */
  167. function getGoodInfo(info) {
  168. return new Promise(function(resolve,reject){
  169. if(!info || !info.goods_id || isNaN(info.goods_id)) return reject("商品ID格式不正确");
  170. dao.show("GoodModel",info.goods_id,function(err,good){
  171. if(err) return reject("获取商品基本信息失败");
  172. good.goods_cat = good.getGoodsCat();
  173. info["good"] = good;
  174. return resolve(info);
  175. });
  176. });
  177. }
  178. /**
  179. * 删除商品图片
  180. *
  181. * @param {[type]} pic 图片对象
  182. * @return {[type]} [description]
  183. */
  184. function removeGoodPic(pic) {
  185. return new Promise(function(resolve,reject) {
  186. if(!pic || !pic.remove) return reject("删除商品图片记录失败");
  187. pic.remove(function(err){
  188. if(err) return reject("删除失败");
  189. resolve();
  190. });
  191. });
  192. }
  193. function removeGoodPicFile(path) {
  194. return new Promise(function(resolve,reject){
  195. fs.unlink(path,function(err,result){
  196. resolve();
  197. });
  198. });
  199. }
  200. function createGoodPic(pic){
  201. return new Promise(function(resolve,reject){
  202. if(!pic) return reject("图片对象不能为空");
  203. var GoodPicModel = dao.getModel("GoodPicModel");
  204. GoodPicModel.create(pic,function(err,newPic){
  205. if(err) return reject("创建图片数据失败");
  206. resolve();
  207. });
  208. });
  209. }
  210. /**
  211. * 更新商品图片
  212. *
  213. * @param {[type]} info 参数
  214. * @param {[type]} newGood 商品基本信息
  215. */
  216. function doUpdateGoodPics(info) {
  217. return new Promise(function(resolve,reject){
  218. var good = info.good;
  219. if(!good.goods_id) return reject("更新商品图片失败");
  220. if(!info.pics) return resolve(info);
  221. dao.list("GoodPicModel",{"columns":{"goods_id":good.goods_id}},function(err,oldpics) {
  222. if(err) return reject("获取商品图片列表失败");
  223. var batchFns = [];
  224. var newpics = info.pics ? info.pics : [];
  225. var newpicsKV = _.keyBy(newpics,"pics_id");
  226. var oldpicsKV = _.keyBy(oldpics,"pics_id");
  227. /**
  228. * 保存图片集合
  229. */
  230. // 需要新建的图片集合
  231. var addNewpics = [];
  232. // 需要保留的图片的集合
  233. var reservedOldpics = [];
  234. // 需要删除的图片集合
  235. var delOldpics = [];
  236. // 如果提交的新的数据中有老的数据的pics_id就说明保留数据,否则就删除
  237. _(oldpics).forEach(function(pic){
  238. if(newpicsKV[pic.pics_id]) {
  239. reservedOldpics.push(pic);
  240. } else {
  241. delOldpics.push(pic);
  242. }
  243. });
  244. // 从新提交的数据中检索出需要新创建的数据
  245. // 计算逻辑如果提交的数据不存在 pics_id 字段说明是新创建的数据
  246. _(newpics).forEach(function(pic){
  247. if(!pic.pics_id && pic.pic) {
  248. addNewpics.push(pic);
  249. }
  250. });
  251. // 开始处理商品图片数据逻辑
  252. // 1. 删除商品图片数据集合
  253. _(delOldpics).forEach(function(pic){
  254. // 1.1 删除图片物理路径
  255. batchFns.push(removeGoodPicFile(path.join(process.cwd(),pic.pics_big)));
  256. batchFns.push(removeGoodPicFile(path.join(process.cwd(),pic.pics_mid)));
  257. batchFns.push(removeGoodPicFile(path.join(process.cwd(),pic.pics_sma)));
  258. // 1.2 数据库中删除图片数据记录
  259. batchFns.push(removeGoodPic(pic));
  260. });
  261. // 2. 处理新建图片的集合
  262. _(addNewpics).forEach(function(pic){
  263. if(!pic.pics_id && pic.pic) {
  264. // 2.1 通过原始图片路径裁剪出需要的图片
  265. var src = path.join(process.cwd(),pic.pic);
  266. var tmp = src.split(path.sep);
  267. var filename = tmp[tmp.length - 1];
  268. pic.pics_big = "/uploads/goodspics/big_" + filename;
  269. pic.pics_mid = "/uploads/goodspics/mid_" + filename;
  270. pic.pics_sma = "/uploads/goodspics/sma_" + filename;
  271. batchFns.push(clipImage(src,path.join(process.cwd(),pic.pics_big),800,800));
  272. batchFns.push(clipImage(src,path.join(process.cwd(),pic.pics_mid),400,400));
  273. batchFns.push(clipImage(src,path.join(process.cwd(),pic.pics_sma),200,200));
  274. pic.goods_id = good.goods_id;
  275. // 2.2 数据库中新建数据记录
  276. batchFns.push(createGoodPic(pic));
  277. }
  278. });
  279. // 如果没有任何图片操作就返回
  280. if(batchFns.length == 0) {
  281. return resolve(info);
  282. }
  283. // 批量执行所有操作
  284. Promise.all(batchFns)
  285. .then(function(){
  286. resolve(info);
  287. })
  288. .catch(function(error){
  289. if(error) return reject(error);
  290. });
  291. });
  292. });
  293. }
  294. function createGoodAttribute(goodAttribute) {
  295. return new Promise(function(resolve,reject) {
  296. dao.create("GoodAttributeModel",_.omit(goodAttribute,"delete_time"),function(err,newAttr){
  297. if(err) return reject("创建商品参数失败");
  298. resolve(newAttr);
  299. });
  300. });
  301. }
  302. /**
  303. * 更新商品属性
  304. *
  305. * @param {[type]} info 参数
  306. * @param {[type]} good 商品对象
  307. */
  308. function doUpdateGoodAttributes(info) {
  309. return new Promise(function(resolve,reject) {
  310. var good = info.good;
  311. if(!good.goods_id) return reject("获取商品图片必须先获取商品信息");
  312. if(!info.attrs) return resolve(info);
  313. // var GoodAttributeModel = dao.getModel("GoodAttributeModel");
  314. goodAttributeDao.clearGoodAttributes(good.goods_id,function(err){
  315. if(err) return reject("清理原始的商品参数失败");
  316. var newAttrs = info.attrs ? info.attrs : [];
  317. if(newAttrs) {
  318. var createFns = [];
  319. _(newAttrs).forEach(function(newattr) {
  320. newattr.goods_id = good.goods_id;
  321. if(newattr.attr_value) {
  322. if(newattr.attr_value instanceof Array) {
  323. newattr.attr_value = newattr.attr_value.join(",");
  324. } else {
  325. newattr.attr_value = newattr.attr_value;
  326. }
  327. }
  328. else
  329. newattr.attr_value = "";
  330. createFns.push(createGoodAttribute(_.clone(newattr)));
  331. });
  332. }
  333. if(createFns.length == 0) return resolve(info);
  334. Promise.all(createFns)
  335. .then(function(){
  336. resolve(info);
  337. })
  338. .catch(function(error){
  339. if(error) return reject(error);
  340. });
  341. });
  342. });
  343. }
  344. /**
  345. * 挂载图片
  346. *
  347. * @param {[type]} info [description]
  348. * @return {[type]} [description]
  349. */
  350. function doGetAllPics(info) {
  351. return new Promise(function(resolve,reject){
  352. var good = info.good;
  353. if(!good.goods_id) return reject("获取商品图片必须先获取商品信息");
  354. // 3. 组装最新的数据挂载在“info”中“good”对象下
  355. dao.list("GoodPicModel",{"columns":{"goods_id":good.goods_id}},function(err,goodPics){
  356. if(err) return reject("获取所有商品图片列表失败");
  357. _(goodPics).forEach(function(pic){
  358. if(pic.pics_big.indexOf("http") == 0) {
  359. pic.pics_big_url = pic.pics_big;
  360. } else {
  361. pic.pics_big_url = upload_config.get("baseURL") + pic.pics_big;
  362. }
  363. if(pic.pics_mid.indexOf("http") == 0) {
  364. pic.pics_mid_url = pic.pics_mid;
  365. } else {
  366. pic.pics_mid_url = upload_config.get("baseURL") + pic.pics_mid;
  367. }
  368. if(pic.pics_sma.indexOf("http") == 0) {
  369. pic.pics_sma_url = pic.pics_sma;
  370. } else {
  371. pic.pics_sma_url = upload_config.get("baseURL") + pic.pics_sma;
  372. }
  373. // pic.pics_mid_url = upload_config.get("baseURL") + pic.pics_mid;
  374. // pic.pics_sma_url = upload_config.get("baseURL") + pic.pics_sma;
  375. });
  376. info.good.pics = goodPics;
  377. resolve(info);
  378. });
  379. });
  380. }
  381. /**
  382. * 挂载属性
  383. * @param {[type]} info [description]
  384. * @return {[type]} [description]
  385. */
  386. function doGetAllAttrs(info) {
  387. return new Promise(function(resolve,reject){
  388. var good = info.good;
  389. if(!good.goods_id) return reject("获取商品图片必须先获取商品信息");
  390. goodAttributeDao.list(good.goods_id,function(err,goodAttrs){
  391. if(err) return reject("获取所有商品参数列表失败");
  392. info.good.attrs = goodAttrs;
  393. resolve(info);
  394. });
  395. });
  396. }
  397. /**
  398. * 创建商品
  399. *
  400. * @param {[type]} params 商品参数
  401. * @param {Function} cb 回调函数
  402. */
  403. module.exports.createGood = function(params,cb) {
  404. // 验证参数 & 生成数据
  405. generateGoodInfo(params)
  406. // 检查商品名称
  407. .then(checkGoodName)
  408. // 创建商品
  409. .then(createGoodInfo)
  410. // 更新商品图片
  411. .then(doUpdateGoodPics)
  412. // 更新商品参数
  413. .then(doUpdateGoodAttributes)
  414. .then(doGetAllPics)
  415. .then(doGetAllAttrs)
  416. // 创建成功
  417. .then(function(info){
  418. cb(null,info.good);
  419. })
  420. .catch(function(err) {
  421. cb(err);
  422. });
  423. }
  424. /**
  425. * 删除商品
  426. *
  427. * @param {[type]} id 商品ID
  428. * @param {Function} cb 回调函数
  429. */
  430. module.exports.deleteGood = function(id,cb) {
  431. if(!id) return cb("产品ID不能为空");
  432. if(isNaN(id)) return cb("产品ID必须为数字");
  433. dao.update(
  434. "GoodModel",
  435. id,
  436. {
  437. 'is_del':'1',
  438. 'delete_time':Date.parse(new Date()) / 1000,
  439. 'upd_time' : Date.parse(new Date()) / 1000
  440. },
  441. function(err){
  442. if(err) return cb(err);
  443. cb(null);
  444. }
  445. );
  446. }
  447. /**
  448. * 获取商品列表
  449. *
  450. * @param {[type]} params 查询条件
  451. * @param {Function} cb 回调函数
  452. */
  453. module.exports.getAllGoods = function(params,cb) {
  454. var conditions = {};
  455. if(!params.pagenum || params.pagenum <= 0) return cb("pagenum 参数错误");
  456. if(!params.pagesize || params.pagesize <= 0) return cb("pagesize 参数错误");
  457. conditions["columns"] = {};
  458. if(params.query) {
  459. conditions["columns"]["goods_name"] = orm.like("%" + params.query + "%");
  460. }
  461. conditions["columns"]["is_del"] = '0';
  462. dao.countByConditions("GoodModel",conditions,function(err,count){
  463. if(err) return cb(err);
  464. pagesize = params.pagesize;
  465. pagenum = params.pagenum;
  466. pageCount = Math.ceil(count / pagesize);
  467. offset = (pagenum - 1) * pagesize;
  468. if(offset >= count) {
  469. offset = count;
  470. }
  471. limit = pagesize;
  472. // 构建条件
  473. conditions["offset"] = offset;
  474. conditions["limit"] = limit;
  475. conditions["only"] = ["goods_id","goods_name","goods_price","goods_weight","goods_state","add_time","goods_number","upd_time","hot_mumber","is_promote"];
  476. conditions["order"] = "-add_time";
  477. dao.list("GoodModel",conditions,function(err,goods){
  478. if(err) return cb(err);
  479. var resultDta = {};
  480. resultDta["total"] = count;
  481. resultDta["pagenum"] = pagenum;
  482. resultDta["goods"] = _.map(goods,function(good){
  483. return _.omit(good,"goods_introduce","is_del","goods_big_logo","goods_small_logo","delete_time");
  484. });
  485. cb(err,resultDta);
  486. })
  487. });
  488. }
  489. /**
  490. * 更新商品
  491. *
  492. * @param {[type]} id 商品ID
  493. * @param {[type]} params 参数
  494. * @param {Function} cb 回调函数
  495. */
  496. module.exports.updateGood = function(id,params,cb) {
  497. params.goods_id = id;
  498. // 验证参数 & 生成数据
  499. generateGoodInfo(params)
  500. // 检查商品名称
  501. .then(checkGoodName)
  502. // 创建商品
  503. .then(updateGoodInfo)
  504. // 更新商品图片
  505. .then(doUpdateGoodPics)
  506. // 更新商品参数
  507. .then(doUpdateGoodAttributes)
  508. .then(doGetAllPics)
  509. .then(doGetAllAttrs)
  510. // 创建成功
  511. .then(function(info){
  512. cb(null,info.good);
  513. })
  514. .catch(function(err) {
  515. cb(err);
  516. });
  517. }
  518. /**
  519. * 更新商品图片
  520. *
  521. * @param {[type]} goods_id 商品ID
  522. * @param {[type]} pics 商品图片
  523. * @param {Function} cb 回调函数
  524. */
  525. module.exports.updateGoodPics = function(goods_id,pics,cb) {
  526. if(!goods_id) return cb("商品ID不能为空");
  527. if(isNaN(goods_id)) return cb("商品ID必须为数字");
  528. getGoodInfo({"goods_id":goods_id,"pics":pics})
  529. .then(doUpdateGoodPics)
  530. .then(doGetAllPics)
  531. .then(doGetAllAttrs)
  532. .then(function(info){
  533. cb(null,info.good);
  534. })
  535. .catch(function(err) {
  536. cb(err);
  537. });
  538. }
  539. module.exports.updateGoodAttributes = function(goods_id,attrs,cb) {
  540. getGoodInfo({"goods_id":goods_id,"attrs":attrs})
  541. .then(doUpdateGoodAttributes)
  542. .then(doGetAllPics)
  543. .then(doGetAllAttrs)
  544. .then(function(info){
  545. cb(null,info.good);
  546. })
  547. .catch(function(err) {
  548. cb(err);
  549. });
  550. }
  551. module.exports.updateGoodsState = function(goods_id,state,cb) {
  552. getGoodInfo({"goods_id":goods_id,"goods_state":state})
  553. .then(updateGoodInfo)
  554. .then(doGetAllPics)
  555. .then(doGetAllAttrs)
  556. .then(function(info){
  557. cb(null,info.good);
  558. })
  559. .catch(function(err) {
  560. cb(err);
  561. });
  562. }
  563. /**
  564. * 通过商品ID获取商品数据
  565. *
  566. * @param {[type]} id 商品ID
  567. * @param {Function} cb 回调函数
  568. */
  569. module.exports.getGoodById = function(id,cb) {
  570. getGoodInfo({"goods_id":id})
  571. .then(doGetAllPics)
  572. .then(doGetAllAttrs)
  573. .then(function(info){
  574. cb(null,info.good);
  575. })
  576. .catch(function(err) {
  577. cb(err);
  578. });
  579. }