insurant.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. 'use strict';
  2. const Service = require('egg').Service;
  3. const Moment = require('moment');
  4. const Const = require("../const");
  5. const InsTableName = "insurance";
  6. const InsCompanyTable = "ins_company";
  7. const InsCmpProdTable = "ins_company_prod";
  8. const DateUnits = ['days','months','years'];
  9. function mergeUpdateField(target, src) {
  10. for( let k in src ) {
  11. if( src[k] === undefined || src === null ) continue;
  12. target[k] = src[k];
  13. }
  14. }
  15. function formatData( data )
  16. {
  17. function formatInt(data, key) {
  18. let value = data[key];
  19. if( value != undefined ) data[key] = parseInt( value );
  20. if( isNaN(data[key])) data[key] = 0;
  21. }
  22. function formatFloat(data, key) {
  23. let value = data[key];
  24. if( value != undefined ) data[key] = parseFloat( value );
  25. if( isNaN(data[key])) data[key] = 0.0;
  26. }
  27. formatFloat(data, "coverage_dur");
  28. formatInt(data, "pay_feq");
  29. formatFloat(data, "pay_per_amt");
  30. formatInt(data, "pay_dur");
  31. formatInt(data, "pay_feq_unit");
  32. formatInt(data, "pay_dur_unit");
  33. }
  34. class InsurantService extends Service {
  35. //上传保单
  36. async addPictures(userid, pictures) {
  37. return "empty";
  38. }
  39. async updateInfoById(id, data) {
  40. let {app } = this;
  41. return await app.mysql.update(InsTableName, data , {where: {id} });
  42. }
  43. async getInfo( _id , columns) {
  44. let {app } = this;
  45. let cols = columns? Object.keys( columns ) : null;
  46. let options = cols? { columns: cols}: null;
  47. return await app.mysql.get(InsTableName, {id: _id}, options);
  48. }
  49. async getDetail(id) {
  50. let {app} = this;
  51. let ret = await app.mysql.get(InsTableName, {id});
  52. if( ret ) {
  53. let prod = await app.mysql.query("select Prod.name as prod_name , Cmp.id as cmpy_id, Cmp.full_name as cmpy_name from ins_company_prod as Prod INNER JOIN ins_company as Cmp on Prod.cmpy_id = Cmp.id where Prod.id=?", [ret.prod_id]);
  54. if( prod[0] ) {
  55. prod = prod[0];
  56. ret.prod_name = prod.prod_name;
  57. ret.cmpy_name = prod.cmpy_name;
  58. ret.cmpy_id = prod.cmpy_id;
  59. }
  60. }
  61. return ret;
  62. }
  63. async getExtendinfo( _id )
  64. {
  65. return "empty";
  66. }
  67. async addbase(userid, data) {
  68. let {ctx } = this;
  69. return "empty";
  70. }
  71. async add(id, data)
  72. {
  73. let { app } = this;
  74. data.user_id = id;
  75. formatData( data );
  76. console.log("adding", data);
  77. await this.updateInsUserNames( data );
  78. this.updateInsCoverageState( data );
  79. this.updateInsPayState( data );
  80. let ret = await app.mysql.insert(InsTableName, data);
  81. return ret;
  82. }
  83. async updateInsUserNames(ins)
  84. {
  85. //coverage_user_names
  86. let mysql = this.app.mysql;
  87. let ids = ins.coverage_user_ids;
  88. let names = [];
  89. ids = ids.split(",");
  90. let n = ids.length;
  91. for( let i=0; i<n; i++) {
  92. let id = ids[i];
  93. let ret = await mysql.get("app_user_family", {id}, {columns:["name"]});
  94. if( ret ) names.push( ret.name );
  95. }
  96. ins.coverage_user_names = names.join(",");
  97. console.log( "names", names, ins.coverage_user_names);
  98. }
  99. //保额保障期限的计算属性
  100. async updateInsCoverageState(ins)
  101. {
  102. //expire_time = effect_time + coverage_dur + coverage_dur_unit
  103. //state = currTime + expire_time + effect_time 0-未生效 1-保障中 2-已失效
  104. // let s = '2020-07-11T02:32:15.154Z';
  105. let t = ins.effect_time;
  106. t = t.split("T")[0];
  107. const effectTime = Moment(t);
  108. const currTime = Moment();
  109. let DurType = ins.coverage_dur_unit;
  110. console.log("effectTime", effectTime, effectTime.daysInMonth);
  111. if( DurType == Const.CoverageDurationType.Other) { //其他
  112. let coverage_birthOrEndDay = ins.coverage_birthOrEndDay;
  113. ins.expire_time = coverage_birthOrEndDay;
  114. } else if( DurType == Const.CoverageDurationType.Life ) { //终身
  115. ins.expire_time = null; //没有过期时间
  116. } else if( DurType == Const.CoverageDurationType.ToAge ) { //到多少岁
  117. let coverage_birthOrEndDay = ins.coverage_birthOrEndDay;//先拿到生日
  118. let age = ins.coverage_dur; //年龄
  119. let end = Moment(coverage_birthOrEndDay).add(age, "years");
  120. ins.expire_time = end.format("YYYY-MM-DD");
  121. } else {
  122. let end = effectTime.add(ins.coverage_dur, DateUnits[ins.coverage_dur_unit]);
  123. ins.expire_time = end.format("YYYY-MM-DD");
  124. }
  125. if( currTime.isBefore( effectTime ) ) ins.state = Const.InsanceState.UnEffect;
  126. else if( currTime.isAfter( Moment(ins.expire_time) ) ) ins.state = Const.InsanceState.Expired;
  127. else ins.state = Const.InsanceState.Effective;
  128. console.log("coverage", ins.state, ins.expire_time);
  129. return ins;
  130. }
  131. //支付相关的时间期限计算属性
  132. async updateInsPayState(ins)
  133. {
  134. //pay_next_time =currTime + paids + effect_time + pay_feq + pay_dur 下次缴费时间 已逾期(小于当前时间) 下次缴费(大于当前) 已缴清(null)
  135. //pay_total = pay_feq + pay_dur + pay_per_amt 保费总数
  136. //pay_end_time = effect_time + pay_feq + pay_dur 最后一次缴费日期
  137. const effectTime = Moment( ins.effect_time );
  138. let payWay = ins.pay_feq_unit;
  139. if( payWay == Const.PayFreqUnit.Dun) { //趸交
  140. ins.pay_end_time = effectTime.format("YYYY-MM-DD");
  141. ins.pay_total = ins.pay_per_amt;
  142. ins.pay_next_time = null;
  143. return;
  144. }
  145. //天 月 年
  146. let endTime = Moment( ins.effect_time ).add(ins.pay_dur, DateUnits[ins.pay_dur_unit]);
  147. let count = 0;
  148. let currTime = effectTime;
  149. let pay_next_time = null;
  150. let now = Moment();
  151. let paids = [];
  152. //todo 防止是循环
  153. do {
  154. count += 1;
  155. let currTimeStr = currTime.format("YYYY-MM-DD");
  156. console.log("currTime", currTime, now, currTime.isBefore(now));
  157. if( currTime.isBefore(now) ) {
  158. paids.push( currTimeStr );
  159. } else if (!pay_next_time) {
  160. pay_next_time = currTime.format("YYYY-MM-DD");
  161. }
  162. currTime = currTime.add( ins.pay_feq, DateUnits[ins.pay_feq_unit]);
  163. } while( currTime.isBefore( endTime ) );
  164. if( paids.length > 0 ) ins.paids = paids.join(",");
  165. ins.pay_total = count * ins.pay_per_amt;
  166. ins.pay_end_time = currTime.format("YYYY-MM-DD");
  167. ins.pay_next_time = pay_next_time;
  168. console.log("pay", ins.pay_total, ins.pay_end_time, ins.pay_next_time);
  169. }
  170. async update(id, data) {
  171. let {app} = this;
  172. let mysql = app.mysql;
  173. let updated = {};
  174. console.log( data );
  175. mergeUpdateField(updated, data);
  176. let ret = await mysql.update(InsTableName, updated, {where: {id: data.id}} );
  177. if( data.paids != undefined ) {//更新 pay_next_time
  178. await this.updateNextPayTime( id );
  179. return "更新成功";
  180. }
  181. return ret;
  182. }
  183. async updateNextPayTime(id){
  184. let mysql = this.app.mysql;
  185. let ins = await mysql.get(InsTableName, {id}, {columns:["effect_time","paids","pay_dur","pay_dur_unit","pay_per_amt","pay_feq","pay_feq_unit","pay_end_time", "pay_next_time"]});
  186. if( !ins ) throw "查询保单失败!";
  187. //重新计算下次缴费时间
  188. let paids =ins.paids? ins.paids.split(","):[];
  189. const effectTime = Moment( ins.effect_time );
  190. let currTime = effectTime;
  191. const now = Moment();
  192. const endTime = Moment( ins.effect_time ).add(ins.pay_dur, DateUnits[ins.pay_dur_unit]);
  193. let payNextDate = null;
  194. do {
  195. let currTimeStr = currTime.format("YYYY-MM-DD");
  196. if( paids.indexOf( currTimeStr ) < 0 )
  197. {//当前没缴费
  198. payNextDate = currTimeStr;
  199. break;
  200. }
  201. currTime = currTime.add( ins.pay_feq, DateUnits[ins.pay_feq_unit]);
  202. } while( currTime.isBefore( endTime ) );
  203. await mysql.update(InsTableName, {pay_next_time:payNextDate}, {where:{id}});
  204. }
  205. async pictureList(id) {
  206. let {app} = this;
  207. let ins = await app.mysql.select(InsTableName,{ where: {user_id: id}, columns: ["pictures"]});
  208. console.log( ins );
  209. let ret = [];
  210. ins.forEach( pic =>{
  211. let p = pic.pictures;
  212. if( p ) {
  213. let arr = p.split(",");
  214. ret = ret.concat(arr);
  215. }
  216. });
  217. return ret;
  218. }
  219. async policyList( uid, type) {
  220. let app = this.app;
  221. //type 类型 0为全部 1 需交费 2 保障中 3 已到期
  222. let select = "SELECT ins.id, curdate() as time,ins.expire_time,ins.coverage_birthOrEndDay,ins.effect_time,ins.coverage_user_names, ins.coverage_dur,ins.coverage_dur_unit, ins.prod_type, ins.pay_total, ins.coverage_amt, ins.coverage_amt_unit, ins.pay_next_time, ins.pay_end_time, ins.coverage_user_ids, Prod.name AS prod_name , Cmp.id AS cmpy_id, Cmp.full_name AS cmpy_name, Cmp.cover AS cover FROM insurance AS ins INNER JOIN ins_company_prod AS Prod ON Prod.id = ins.prod_id INNER JOIN ins_company AS Cmp ON Cmp.id = Prod.`cmpy_id` WHERE ins.user_id=?"
  223. if( type == undefined || type == 0) {
  224. return await app.mysql.query(`${select} LIMIT 0, 10`,[uid]);
  225. } else if( type == 1) {
  226. return await app.mysql.query(`${select} and pay_next_time IS TRUE LIMIT 0, 10`,[uid]);
  227. } else if( type == 2) {
  228. return await app.mysql.query(`${select} and state=1 LIMIT 0, 10`,[uid]);
  229. } else if( type == 3) {
  230. return await app.mysql.query(`${select} and state=2 LIMIT 0, 10`,[uid]);
  231. }
  232. throw "未知的查询类型";
  233. }
  234. //保额分布
  235. async distributed(uid, userId)
  236. {
  237. let {app} = this;
  238. let ret = "";
  239. if( !userId ) {
  240. ret = await app.mysql.query("select prod_type, sum(CASE WHEN coverage_amt_unit = 0 THEN 10000 * coverage_amt ELSE coverage_amt end) as amt from `insurance` where user_id=? and ( coverage_amt_unit = 0 or coverage_amt_unit=1) GROUP BY prod_type",[uid]);
  241. } else {
  242. ret = await app.mysql.query("select prod_type, sum(CASE WHEN coverage_amt_unit = 0 THEN 10000 * coverage_amt ELSE coverage_amt end) as amt from `insurance` where user_id=? AND FIND_IN_SET(?,coverage_user_ids) > 0 and ( coverage_amt_unit = 0 or coverage_amt_unit=1) GROUP BY prod_type",[uid, userId]);
  243. }
  244. console.log("distributed", uid, userId, ret);
  245. return ret;
  246. }
  247. async policyStatics(uid) {
  248. //总保单数 SELECT COUNT(id) FROM `insurance` WHERE user_id=1
  249. //需要缴费 SELECT COUNT(id) FROM `insurance` WHERE user_id=1 AND pay_next_time IS TRUE
  250. //保障中 SELECT COUNT(id) FROM `insurance` WHERE user_id=1 AND state=1
  251. //已到期 SELECT COUNT(id) FROM `insurance` WHERE user_id=1 AND state=2
  252. let {app} = this;
  253. let ret = await app.mysql.query("SELECT COUNT(id) AS total, SUM(CASE WHEN pay_next_time IS TRUE THEN 1 ELSE 0 END) AS pay,SUM(CASE WHEN state=1 THEN 1 ELSE 0 END) AS valid ,SUM(CASE WHEN state=2 THEN 1 ELSE 0 END) AS expire FROM `insurance` WHERE user_id=?",[uid]);
  254. let result = ret[0];
  255. if( result.total == 0 ) {
  256. result.pay = 0;
  257. result.valid = 0;
  258. result.expire = 0;
  259. }
  260. return result;
  261. }
  262. //todo 优化这里的统计数据使用缓存
  263. async updateYearPayInfoCache(uid, fid) {
  264. let app = this.app;
  265. let startOf = Moment().startOf('year').format("YYYY-MM-DD");
  266. //获取用户的所有本年需要缴费的保单
  267. let ins =[];
  268. if( !fid ) ins = await app.mysql.query("SELECT effect_time, pay_feq, pay_feq_unit, pay_per_amt, pay_per_amt_unit, pay_dur, pay_dur_unit, paids FROM `insurance` WHERE user_id=? and pay_next_time is true and Date(pay_end_time)>?",[uid, startOf]);
  269. else {
  270. ins = await app.mysql.query("SELECT effect_time, pay_feq, pay_feq_unit, pay_per_amt, pay_per_amt_unit, pay_dur, pay_dur_unit, paids FROM `insurance` WHERE user_id=? and pay_next_time is true and Date(pay_end_time)>? and FIND_IN_SET(?,coverage_user_ids) > 0",[uid, startOf, fid]);
  271. }
  272. let rmbs = [];
  273. let dollers = [];
  274. ins.forEach(item=>{
  275. if( item.pay_per_amt_unit == 2) dollers.push( item );
  276. else {
  277. rmbs.push( item );
  278. }
  279. });
  280. function calc( rows ) {
  281. const defaultDate = Moment();
  282. let currYear = defaultDate.year();
  283. let ret = {total: 0, payed:0, year:currYear};
  284. rows.forEach( r =>{
  285. let effectiveTime = r.effect_time;
  286. const currentDate = Moment(effectiveTime);
  287. const endDate = Moment(effectiveTime).add(r.pay_dur, DateUnits[r.pay_dur_unit]);
  288. // let payList = [defaultDate];
  289. do {
  290. const currDateFmt = currentDate.format('YYYY-MM')
  291. // payList.push(currDateFmt);
  292. let payYear = currentDate.year();
  293. if( payYear <= currYear ) {
  294. ret.total += r.pay_per_amt;
  295. if( r.pay_feq_unit == 3 ) { //趸交,默认已交
  296. ret.payed += r.pay_per_amt;
  297. break;
  298. }
  299. if( r.paids && r.paids.indexOf( currDateFmt ) > 0 ) {
  300. ret.payed += r.pay_per_amt;
  301. }
  302. }
  303. currentDate.add(r.pay_feq, DateUnits[r.pay_feq_unit]);
  304. } while (currentDate.isBefore(endDate));
  305. })
  306. return ret;
  307. }
  308. let rmb = calc( rmbs );
  309. let doller = calc( dollers );
  310. return {rmb, doller};
  311. }
  312. //今年保费缴纳情况
  313. async payInfo( uid, fid ) {
  314. //当年的缴费状态存redis,保单添加和缴费状态修改的时候,更新缓存
  315. //获取当年的缴费表
  316. return await this.updateYearPayInfoCache( uid , fid);
  317. // const currentDate = Moment(effectiveTime);
  318. // const endDate = Moment(effectiveTime).add(payDuration.num, dateUnits[payDuration.unit]);
  319. // const defaultDate = Moment();
  320. // let payList = [];
  321. // let paidList = [];
  322. // do {
  323. // const currDateFmt = currentDate.format('YYYY-MM')
  324. // payList.push(currDateFmt);
  325. // if(currentDate.isBefore(defaultDate)){
  326. // paidList.push(currDateFmt)
  327. // }
  328. // currentDate.add(payMethod.num, dateUnits[payMethod.unit])
  329. // } while (currentDate.isBefore(endDate));
  330. // if(!(paids && !paidsRef.current)){
  331. // onChange && onChange(paidList);
  332. // }
  333. // paidsRef.current = paidList;
  334. // setList(payList);
  335. }
  336. //用户的保单诊断
  337. async diagnose(uid) {
  338. return {
  339. totalScore: 98,
  340. point1: 20,
  341. point2: 20,
  342. point3: 20,
  343. point4: 20,
  344. point5: 20,
  345. risk:89.2,
  346. level: 20,
  347. title: "uid文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案" + uid,
  348. advice:"文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案,文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案,文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案文案"
  349. }
  350. }
  351. //完成pay_next_day缴费 影响 pay_next_day 和 paids 两个字段
  352. async payed( id ) {
  353. let mysql = this.app.mysql;
  354. let ins = await mysql.get(InsTableName, {id}, {columns:["effect_time","paids","pay_dur","pay_dur_unit","pay_per_amt","pay_feq","pay_feq_unit","pay_end_time", "pay_next_time"]});
  355. if( !ins ) throw "查询保单失败!";
  356. //重新计算下次缴费时间
  357. let pay_next_time = ins.pay_next_time;
  358. if( !pay_next_time ) return ins; //已经缴清
  359. let payNextDate = Moment( pay_next_time ).format("YYYY-MM-DD");
  360. let paids = [];
  361. if( !ins.paids ) ins.paids = "";
  362. paids = ins.paids.split(",");
  363. if( paids.indexOf(payNextDate) < 0 ) {
  364. paids.push(payNextDate);
  365. }
  366. ins.paids = paids.join(",");
  367. const effectTime = Moment( ins.effect_time );
  368. let currTime = effectTime;
  369. const now = Moment();
  370. const endTime = Moment( ins.effect_time ).add(ins.pay_dur, DateUnits[ins.pay_dur_unit]);
  371. payNextDate = null;
  372. do {
  373. let currTimeStr = currTime.format("YYYY-MM-DD");
  374. console.log("currTime", currTime, now, currTime.isBefore(now));
  375. if( paids.indexOf( currTimeStr ) < 0 )
  376. {//当前没缴费
  377. payNextDate = currTimeStr;
  378. break;
  379. }
  380. currTime = currTime.add( ins.pay_feq, DateUnits[ins.pay_feq_unit]);
  381. } while( currTime.isBefore( endTime ) );
  382. ins.pay_next_time = payNextDate;
  383. await mysql.update(InsTableName, {pay_next_time:ins.pay_next_time, paids: ins.paids}, {where:{id}});
  384. return {pay_next_time: payNextDate};
  385. }
  386. async list(userid, query) {
  387. let app = this.app;
  388. //type 类型 0为全部 1 需交费 2 保障中 3 已到期
  389. let select = "SELECT ins.id, curdate() as time,ins.expire_time, ins.coverage_birthOrEndDay,ins.effect_time,ins.coverage_user_names, ins.coverage_dur,ins.coverage_dur_unit, ins.prod_type, ins.pay_total, ins.coverage_amt, ins.coverage_amt_unit, ins.pay_next_time, ins.pay_end_time, ins.coverage_user_ids, Prod.name AS prod_name , Cmp.id AS cmpy_id, Cmp.full_name AS cmpy_name, Cmp.cover AS cover FROM insurance AS ins INNER JOIN ins_company_prod AS Prod ON Prod.id = ins.prod_id INNER JOIN ins_company AS Cmp ON Cmp.id = Prod.`cmpy_id` WHERE ins.user_id=?"
  390. let where = "";
  391. if( query.type != undefined) {
  392. where += `and ins.prod_type=${query.type} `;
  393. }
  394. if( query.uid != undefined) {
  395. where += `and FIND_IN_SET(${query.uid}, ins.coverage_user_ids) > 0 `;
  396. }
  397. if( query.state != undefined) {
  398. where += `and ins.state=${query.state} `;
  399. }
  400. let orderBy = "";
  401. if( query.order != undefined ) {
  402. switch(query.order) {
  403. case 0: orderBy = "ORDER BY ins.pay_next_time DESC";
  404. break;
  405. case 1: orderBy = "ORDER BY ins.pay_next_time";
  406. break;
  407. case 2: orderBy = "ORDER BY ins.expire_time DESC";
  408. break;
  409. case 3: orderBy = "ORDER BY ins.expire_time";
  410. }
  411. }
  412. console.log( "-------------------------------", query);
  413. let sql = `${select} ${where} ${orderBy} LIMIT ${query.page*query.size},${query.size}`;
  414. console.log( sql );
  415. return await app.mysql.query(sql,[userid]);
  416. }
  417. }
  418. module.exports = InsurantService;