Lesson.php 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064
  1. <?php
  2. namespace app\api\controller;
  3. use app\common\controller\Api;
  4. use think\Db;
  5. /**
  6. * 售课
  7. */
  8. class Lesson extends Api
  9. {
  10. // 无需登录的接口,*表示全部
  11. protected $noNeedLogin = [];
  12. // 无需鉴权的接口,*表示全部
  13. protected $noNeedRight = ['*'];
  14. //售课列表
  15. public function lists(){
  16. $list = Db::name('lesson')->where('is_show',1)->order('weigh desc')->autopage()->select();
  17. $list = list_domain_image($list,['image']);
  18. $list = $this->list_lang($list,['name','title','info','shiherenqun','pingjunrenshu','sirenkecheng','daoshi']);
  19. $this->success(1,$list);
  20. }
  21. //售课分类列表
  22. public function lesson_cate(){
  23. $list = Db::name('lesson_cate')->order('id asc')->select();
  24. $list = $this->list_lang($list,['name']);
  25. $this->success(1,$list);
  26. }
  27. //日历列表
  28. //教练列表
  29. public function coach_list(){
  30. $list = Db::name('coach')->field('id,nickname,avatar')->where('status',1)->order('nickname asc')->select();
  31. $list = list_domain_image($list,['avatar']);
  32. $this->success(1,$list);
  33. }
  34. //课时列表首页
  35. //根据年,月获取当月剩余日期,有课程的标记1
  36. public function slot_index(){
  37. $getdate = input('date',date('Y-m'));
  38. if(empty($getdate)){
  39. $getdate = date('Y-m');
  40. }
  41. $year = substr($getdate,0,4);
  42. $month = substr($getdate,-2,2);
  43. //今天是哪日
  44. if($year == date('Y') && $month == date('m')){
  45. //如果是今年今月,默认今天开始
  46. $startday = date('d');
  47. }else{
  48. //从1号开始
  49. $startday = '01';
  50. }
  51. //所有课程点进来的 额外传参
  52. $lesson_id = input('lesson_id',0);
  53. if($lesson_id){
  54. $first_starttime = Db::name('lesson_slot')->where('lesson_id',$lesson_id)->where('status',0)->where('starttime','gt',time())->where('is_show',1)->order('starttime asc')->value('starttime');
  55. // $first_startdate = date('Y-m-d H:i:s',$first_starttime);dump($first_startdate);exit;
  56. if($first_starttime){
  57. $year = date('Y',$first_starttime);
  58. $month = date('m',$first_starttime);
  59. $startday = date('d',$first_starttime);
  60. }
  61. }
  62. //一周
  63. $week_arr = [
  64. 1 => '周一',
  65. 2 => '周二',
  66. 3 => '周三',
  67. 4 => '周四',
  68. 5 => '周五',
  69. 6 => '周六',
  70. 7 => '周日',
  71. ];
  72. //本月结束日
  73. $endday_arr = [
  74. '01' => 31,
  75. '02' => 28,
  76. '03' => 31,
  77. '04' => 30,
  78. '05' => 31,
  79. '06' => 30,
  80. '07' => 31,
  81. '08' => 31,
  82. '09' => 30,
  83. '10' => 31,
  84. '11' => 30,
  85. '12' => 31,
  86. ];
  87. if($year%4 == 0){
  88. $endday_arr['02'] = 29;
  89. }
  90. $endday = $endday_arr[$month];
  91. //拿数据
  92. $startdate = $getdate.'-'.$startday;
  93. $enddate = $getdate.'-'.$endday;
  94. $where = [
  95. 'starttime' => ['BETWEEN',[strtotime($startdate),strtotime($enddate)+86399]],
  96. 'status' => 0,
  97. 'is_show' => 1,
  98. ];
  99. //dump($where);exit;
  100. $slots = Db::name('lesson_slot')->where($where)->field('id,starttime')->select();
  101. $date_arr = [];
  102. for($i=intval($startday);$i<=$endday;$i++){
  103. $j = $i;
  104. if($j < 10){
  105. $j = 0 . $j;
  106. }
  107. $idate = $year.'-'.$month.'-'.$j;
  108. $itime = strtotime($idate);
  109. $week = $this->lang == 'en' ? date('D',$itime) : $week_arr[date('N',$itime)];
  110. $i_data = [
  111. 'yeah' =>$year,
  112. 'month'=>$month,
  113. 'day' => $j.'',
  114. 'date' => $idate,
  115. 'week' => $week,
  116. 'is_today' => 0,
  117. 'slot_num' => 0,
  118. 'date_show'=> $this->lang == 'en' ? $j.'.'.$month : $month.'.'.$j,
  119. ];
  120. foreach($slots as $slot){
  121. if(date('Y-m-d',$slot['starttime']) == $idate)
  122. {
  123. $i_data['slot_num'] += 1;
  124. }
  125. }
  126. if($idate == date('Y-m-d')){
  127. $i_data['is_today'] = 1;
  128. }
  129. $date_arr[] = $i_data;
  130. }
  131. $this->success(1,$date_arr);
  132. }
  133. //某日课时列表
  134. public function slot_list(){
  135. $date = strtotime(input('date',date('Y-m-d')));
  136. $cate_id = input('cate_id',0);
  137. $coach_id = input('coach_id',0);
  138. //所有课程点进来的 额外传参
  139. $lesson_id = input('lesson_id',0);
  140. $where = [
  141. 'slot.starttime' => ['BETWEEN',[$date,$date+86399]],
  142. 'slot.status' => 0,
  143. 'slot.is_show' => 1,
  144. ];
  145. //课时
  146. $list = Db::name('lesson_slot')->alias('slot')
  147. ->field('slot.*,lesson.name,lesson.name_en,lesson.image,lesson.price')
  148. ->join('lesson','slot.lesson_id = lesson.id','LEFT')
  149. ->where($where);
  150. if($lesson_id){
  151. $list->where('slot.lesson_id',$lesson_id);
  152. }else{
  153. if($cate_id){
  154. $list->where('lesson.lessoncate_id',$cate_id);
  155. }
  156. if($coach_id){
  157. $list->where('find_in_set(:coach_ids,coach_ids)', ['coach_ids' => $coach_id]);
  158. }
  159. }
  160. $list = $list->order('slot.starttime asc')->select();
  161. if(empty($list)){
  162. $this->success(1,[]);
  163. }
  164. $list = list_domain_image($list,['image']);
  165. $list = $this->list_lang($list,['name']);
  166. //准备教练数据
  167. $coach_list = Db::name('coach')->column('id,nickname');
  168. foreach($list as $key => &$slot){
  169. //hours转换
  170. $slot['hours'] = floatval($slot['hours']);
  171. //放入教练
  172. $coach_text = '';
  173. $coach_ids = explode(',',$slot['coach_ids']);
  174. foreach($coach_ids as $coach_id){
  175. if(isset($coach_list[$coach_id])){
  176. $coach_text .= $coach_list[$coach_id].',';
  177. }
  178. }
  179. $slot['coach_text'] = substr($coach_text,0,-1);
  180. //剩余空位数量
  181. $pay_number = Db::name('lesson_order')->where('slot_id',$slot['id'])->where('order_status',10)->sum('usernumber');
  182. $slot['num_remain'] = $slot['num_max'] - $pay_number;
  183. if($slot['num_remain'] < 0){
  184. $slot['num_remain'] = 0;
  185. }
  186. //剩余候补数量
  187. $slot['wait_remain'] = 0;
  188. if($slot['waitnum_max'] > 0){
  189. $wait_number = Db::name('lesson_order')->where('slot_id',$slot['id'])->where('jointype',2)->sum('usernumber');
  190. $slot['wait_remain'] = $slot['waitnum_max'] - $wait_number;
  191. if($slot['wait_remain'] < 0){
  192. $slot['wait_remain'] = 0;
  193. }
  194. }
  195. //预约按钮,1能预约,0不能
  196. $slot['button_status'] = 0;
  197. if($slot['status'] == 0 && $slot['num_remain'] > 0){
  198. $slot['button_status'] = 1;//能预约
  199. }
  200. if($slot['status'] == 0 && $slot['wait_remain'] > 0){
  201. $slot['button_status'] = 2;//能候补
  202. }
  203. //右上角备注
  204. if(time() >= $slot['starttime']){
  205. $slot['num_remark'] = __('报名已截止');
  206. $slot['button_status'] = 0;//报名截止了,强制改
  207. }else{
  208. // $slot['num_remark'] = __('剩N个名额',['number'=>$slot['num_remain']]);
  209. $slot['num_remark'] = __('预约:').$pay_number.'/'.$slot['num_max'];
  210. if($slot['waitnum_max'] > 0){
  211. $slot['num_remark'] .= '--'. __('候补:') .$wait_number.'/'.$slot['waitnum_max'];
  212. }
  213. }
  214. }
  215. $this->success(1,$list);
  216. }
  217. public function test(){
  218. echo __('剩N个名额',['number'=>8]);
  219. }
  220. //课时详情
  221. public function slot_info(){
  222. $slot_id = input('slot_id',0);
  223. $number = input('number',1,'intval');
  224. //课时
  225. $info = Db::name('lesson_slot')->alias('slot')
  226. ->field('slot.*,lesson.name,lesson.name_en,lesson.image,lesson.price')
  227. ->join('lesson','slot.lesson_id = lesson.id','LEFT')
  228. ->where('slot.id',$slot_id)->where('slot.is_show',1)->find();
  229. if(empty($info)){
  230. $this->error('课程可能已取消,请刷新重试');
  231. }
  232. $info = info_domain_image($info,['image']);
  233. $info = $this->info_lang($info,['name']);
  234. //准备教练数据
  235. $coach_list = Db::name('coach')->where('id','IN',$info['coach_ids'])->column('nickname');
  236. $info['coach_text'] = implode(',',$coach_list);
  237. //剩余空位数量
  238. $pay_number = Db::name('lesson_order')->where('slot_id',$info['id'])->where('order_status',10)->sum('usernumber');
  239. $info['num_remain'] = $info['num_max'] - $pay_number;
  240. if($info['num_remain'] < 0){
  241. $info['num_remain'] = 0;
  242. }
  243. //剩余候补数量
  244. $info['wait_remain'] = 0;
  245. $info['wait_number'] = 0;
  246. if($info['waitnum_max'] > 0){
  247. $wait_number = Db::name('lesson_order')->where('slot_id',$info['id'])->where('jointype',2)->sum('usernumber');
  248. $info['wait_number'] = $wait_number;
  249. $info['wait_remain'] = $info['waitnum_max'] - $wait_number;
  250. if($info['wait_remain'] < 0){
  251. $info['wait_remain'] = 0;
  252. }
  253. }
  254. //时间
  255. if($this->lang == 'en'){
  256. $info['slot_time'] = date('M d',$info['starttime']).'('.date('D',$info['starttime']).')'.','.date('H:i',$info['starttime']).'-'.date('H:i',$info['endtime']);
  257. }else{
  258. $info['slot_time'] = date('n月d',$info['starttime']).'(周'.date('N',$info['starttime']).')'.','.date('H:i',$info['starttime']).'-'.date('H:i',$info['endtime']);
  259. }
  260. //是否有可买配套
  261. $wait_buy_package = Db::name('lesson_package')
  262. ->where('find_in_set(:lesson_ids,lesson_ids)', ['lesson_ids' => $info['lesson_id']])
  263. ->where('is_show',1)
  264. ->find();
  265. $info['wait_buy_package'] = !empty($wait_buy_package) ? 1 : 0;
  266. //预约按钮,1能预约,0不能
  267. $info['button_status'] = 0;
  268. if($info['status'] == 0 && $info['num_remain'] > 0){
  269. $info['button_status'] = 1;//能预约
  270. }
  271. if($info['status'] == 0 && $info['wait_remain'] > 0){
  272. $info['button_status'] = 2;//能候补
  273. }
  274. //右上角备注
  275. if(time() >= $info['starttime']){
  276. $info['button_status'] = 0;//报名截止了,强制改
  277. }
  278. //总价格乘出来
  279. $info['price'] = bcmul($info['price'],$number,2);
  280. //此课程是否还有未使用的套餐(多个包含赠送的)
  281. $number_hours = bcmul($number,$info['hours'],1);//每日课时按小时扣
  282. $map = [
  283. 'o.user_id' =>$this->auth->id,
  284. 'o.endtime' => ['gt',time()],
  285. 'o.remain' => ['egt',$number_hours], //不能只是大于0,还得大于等于报名人数
  286. 'o.order_status' => 1,
  287. // 'o.use_status' => 1, //已激活的。未激活的也要展示,但是变灰
  288. ];
  289. $package_list = Db::name('package_order')->alias('o')
  290. ->join('lesson_package p','o.package_id = p.id','LEFT')
  291. ->field('o.id,o.remain,o.starttime,o.endtime,o.is_gift,p.name,p.name_en')
  292. ->where($map)
  293. ->where('find_in_set(:lesson_ids,o.lesson_ids)', ['lesson_ids' => $info['lesson_id']])
  294. ->order('o.endtime asc')->select();
  295. $package_list = $this->list_lang($package_list,['name']);
  296. if(!empty($package_list)){
  297. foreach($package_list as $key => &$val){
  298. //转换remain
  299. $val['remain'] = floatval($val['remain']);
  300. if($this->lang == 'zh-cn'){
  301. $val['time_text'] = date('m月d,Y',$val['starttime']).'-'.date('m月d,Y',$val['endtime']);
  302. }else{
  303. $val['time_text'] = date('M d,Y',$val['starttime']).'-'.date('M d,Y',$val['endtime']);
  304. }
  305. }
  306. }
  307. $info['package_list'] = $package_list;
  308. //可用试课
  309. if($number == 1){
  310. $try_map = [
  311. 'o.user_id' => $this->auth->id,
  312. 'o.endtime' => ['gt',time()],
  313. 'o.order_status' => 10,
  314. ];
  315. $tryorder_list = Db::name('trylesson_order')->alias('o')
  316. ->join('trylesson','o.trylesson_id = trylesson.id','LEFT')
  317. ->field('o.id,o.starttime,o.endtime,trylesson.name,trylesson.name_en')
  318. ->where($try_map)
  319. ->where('find_in_set(:lesson_ids,o.lesson_ids)', ['lesson_ids' => $info['lesson_id']])
  320. ->order('o.endtime asc')->select();
  321. $tryorder_list = $this->list_lang($tryorder_list,['name']);
  322. if(!empty($tryorder_list)){
  323. foreach($tryorder_list as $key => &$val){
  324. if($this->lang == 'zh-cn'){
  325. $val['time_text'] = date('m月d,Y',$val['starttime']).'-'.date('m月d,Y',$val['endtime']);
  326. }else{
  327. $val['time_text'] = date('M d,Y',$val['starttime']).'-'.date('M d,Y',$val['endtime']);
  328. }
  329. }
  330. }
  331. }else{
  332. $tryorder_list = [];
  333. }
  334. $info['tryorder_list'] = $tryorder_list;
  335. //hours转换
  336. $info['hours'] = floatval($info['hours']);
  337. $this->success(1,$info);
  338. }
  339. //课时申请报名,提前预判
  340. public function slot_apply_before(){
  341. $this->apiLimit();
  342. $slot_id = input('slot_id',0,'intval');
  343. $number = input('number',1,'intval');
  344. if($number <= 0){
  345. $this->error('预约人数错误');
  346. }
  347. //课时
  348. $info = Db::name('lesson_slot')->alias('slot')
  349. ->field('slot.*,lesson.name,lesson.name_en,lesson.image,lesson.price')
  350. ->join('lesson','slot.lesson_id = lesson.id','LEFT')
  351. ->where('slot.id',$slot_id)->where('slot.status',0)->where('slot.is_show',1)->find();
  352. if(empty($info)){
  353. $this->error('课程可能已取消,请刷新重试');
  354. }
  355. if($info['endtime'] < time()){
  356. $this->error('课程已经结束了,不能再进行预约');
  357. }
  358. //剩余空位数量
  359. $pay_number = Db::name('lesson_order')->where('slot_id',$slot_id)->where('order_status',10)->sum('usernumber');
  360. $num_remain = $info['num_max'] - $pay_number;
  361. if($num_remain < 0){
  362. $num_remain = 0;
  363. }
  364. //预约数量足够
  365. if($num_remain >= $number){
  366. $this->success('success',1);
  367. }
  368. //只要还有预约位置,就不能候补
  369. if($num_remain > 0){
  370. $this->error('可预约名额不足');
  371. }
  372. //剩余候补数量
  373. $wait_remain = 0;
  374. if($info['waitnum_max'] > 0){
  375. $wait_number = Db::name('lesson_order')->where('slot_id',$info['id'])->where('jointype',2)->sum('usernumber');
  376. $wait_remain = $info['waitnum_max'] - $wait_number;
  377. if($wait_remain < 0){
  378. $wait_remain = 0;
  379. }
  380. }
  381. //候补数量足够
  382. if($wait_remain >= $number){
  383. $this->success('success',2);
  384. }
  385. //候补也不够了
  386. $this->error('可候补名额不足');
  387. }
  388. //课时申请报名
  389. public function slot_apply(){
  390. $this->apiLimit();
  391. $slot_id = input('slot_id',0,'intval');
  392. $number = input('number',1,'intval');
  393. $remark = input('remark','','trim');
  394. $paytype = input('paytype',1,'intval');//支付类型:1=课程套餐,2=线上付款,3=购买套餐中,4=试课单,5=候补
  395. $packageorder_id = input('packageorder_id',0,'intval');
  396. $trylessonorder_id = input('trylessonorder_id',0,'intval');
  397. $jointype = input('jointype',1);//预约类型:1=预约,2=候补
  398. if($number <= 0){
  399. $this->error('预约人数错误');
  400. }
  401. if(!in_array($paytype,[1,2,3,4])){
  402. $this->error();
  403. }
  404. if(!in_array($jointype,[1,2])){
  405. $this->error();
  406. }
  407. //课时
  408. $info = Db::name('lesson_slot')->alias('slot')
  409. ->field('slot.*,lesson.name,lesson.name_en,lesson.image,lesson.price')
  410. ->join('lesson','slot.lesson_id = lesson.id','LEFT')
  411. ->where('slot.id',$slot_id)->where('slot.status',0)->where('slot.is_show',1)->find();
  412. if(empty($info)){
  413. $this->error('课程可能已取消,请刷新重试');
  414. }
  415. if($info['endtime'] < time()){
  416. $this->error('课程已经结束了,不能再进行预约');
  417. }
  418. //剩余空位数量
  419. $pay_number = Db::name('lesson_order')->where('slot_id',$slot_id)->where('order_status',10)->sum('usernumber');
  420. $num_remain = $info['num_max'] - $pay_number;
  421. if($num_remain < 0){
  422. $num_remain = 0;
  423. }
  424. //剩余候补数量
  425. $wait_remain = 0;
  426. if($info['waitnum_max'] > 0){
  427. $wait_number = Db::name('lesson_order')->where('slot_id',$info['id'])->where('jointype',2)->sum('usernumber');
  428. $wait_remain = $info['waitnum_max'] - $wait_number;
  429. if($wait_remain < 0){
  430. $wait_remain = 0;
  431. }
  432. }
  433. //报名人数不能超限
  434. if($jointype == 1){
  435. if($num_remain < $number){
  436. $this->error(__('预约名额只剩N名',['number'=>$num_remain]));
  437. }
  438. //可以继续
  439. }else{
  440. if($num_remain > 0){
  441. $this->error(__('预约还有名额,不能候补'));
  442. }
  443. //候补人数不能超限
  444. if($wait_remain < $number){
  445. $this->error(__('候补名额只剩N名',['number'=>$wait_remain]));
  446. }
  447. //可以继续
  448. $paytype = 5;//强改支付方式,候补
  449. }
  450. //
  451. $number_hours = bcmul($number,$info['hours'],1);//每日课时按小时扣
  452. $lesson_order = [
  453. 'order_no' => createUniqueNo('S',$this->auth->id),
  454. 'user_id' => $this->auth->id,
  455. 'slot_id' => $slot_id,
  456. 'lesson_id' => $info['lesson_id'],
  457. 'order_amount' => bcmul($info['price'],$number,2),
  458. 'order_status' => 0,
  459. 'paytype' => $paytype, //支付类型:1=课程套餐,2=线上付款,3=购买套餐中,4=试课单,5=候补
  460. 'paytime' => 0,
  461. 'createtime' => time(),
  462. // 'updatetime' => ,
  463. // 'finishtime' => ,
  464. 'usernumber' => $number,
  465. 'usernumber_hours' => $number_hours,
  466. 'userremark' => $remark,
  467. 'package_order_id' => 0,
  468. 'package_remark' => '',// 比如:5/30,5-7/30
  469. 'jointype' => $jointype,
  470. ];
  471. //
  472. Db::startTrans();
  473. if($paytype == 1){
  474. //检查已选择套餐
  475. $map = [
  476. 'user_id' =>$this->auth->id,
  477. 'endtime' => ['gt',time()],
  478. 'remain' => ['gt',0],
  479. 'order_status' => 1,
  480. // 'use_status' => 1, //已激活的
  481. 'id' => $packageorder_id,
  482. ];
  483. $package_order = Db::name('package_order')->where($map)->where('find_in_set(:lesson_ids,lesson_ids)', ['lesson_ids' => $info['lesson_id']])->lock(true)->find();
  484. if(!$package_order){
  485. Db::rollback();
  486. $this->error('配套信息不正确,请刷新重试');
  487. }
  488. //课时不足支撑报名人数
  489. if($package_order['remain'] < $number_hours){
  490. Db::rollback();
  491. $this->error('该配套余额不足,可以使用其他支付方式');
  492. }
  493. if($package_order['use_status'] != 1){
  494. Db::rollback();
  495. $this->error('该配套尚未激活');
  496. }
  497. //扣除一节
  498. $update = [
  499. 'remain' => bcsub($package_order['remain'],$number_hours,1),
  500. 'updatetime' => time(),
  501. ];
  502. $rs1 = Db::name('package_order')->where('id',$packageorder_id)->update($update);
  503. if($rs1 === false){
  504. Db::rollback();
  505. $this->error('扣除套餐余额失败');
  506. }
  507. //修改预约单数据
  508. $lesson_order['order_amount'] = 0;
  509. $lesson_order['order_status'] = 10;
  510. $lesson_order['paytime'] = time();
  511. $lesson_order['package_order_id'] = $packageorder_id;
  512. $lesson_order['package_remark'] = ($package_order['sessions'] - $package_order['remain']) . '-' . ($package_order['sessions'] - $package_order['remain'] + $number_hours) .'/'. $package_order['sessions'];
  513. //预约单写入
  514. $lesson_order_id = Db::name('lesson_order')->insertGetId($lesson_order);
  515. if(!$lesson_order_id){
  516. Db::rollback();
  517. $this->error('预约失败');
  518. }
  519. //更新已预约人数
  520. $pay_number = Db::name('lesson_order')->where('slot_id',$slot_id)->where('order_status',10)->sum('usernumber');
  521. $rs_slot = Db::name('lesson_slot')->where('id',$slot_id)->update(['bookednum' => $pay_number]);
  522. if($rs_slot === false){
  523. Db::rollback();
  524. $this->error('预约失败');
  525. }
  526. Db::commit();
  527. $this->success('预约成功',['returntype'=>1]);
  528. }
  529. elseif($paytype == 2)
  530. {
  531. //预约单写入
  532. $lesson_order_id = Db::name('lesson_order')->insertGetId($lesson_order);
  533. if(!$lesson_order_id){
  534. Db::rollback();
  535. $this->error('预约失败');
  536. }
  537. //支付订单下单
  538. $pay_order = [];
  539. $pay_order['user_id'] = $lesson_order['user_id'];
  540. $pay_order['out_trade_no'] = $lesson_order['order_no'];
  541. $pay_order['order_amount'] = $lesson_order['order_amount'];
  542. $pay_order['createtime'] = $lesson_order['createtime'];
  543. $pay_order['pay_type'] = 'hitpay';
  544. $pay_order['platform'] = 'app';
  545. $pay_order['order_status'] = 0;
  546. $pay_order['table_name'] = 'lesson_order';
  547. $pay_order['table_id'] = $lesson_order_id;
  548. $pay_order['args'] = '';
  549. //拉起支付
  550. $notify_url = config('notify_cdnurl');
  551. $redirect_cdnurl = config('hitpay.lesson_redirect_cdnurl').'?out_trade_no='.$pay_order['out_trade_no'].'&lang='.$this->lang;
  552. $hitpay_return = $this->hitpay_payment($pay_order['out_trade_no'],$pay_order['order_amount'],$notify_url,$redirect_cdnurl);
  553. if($hitpay_return['status'] !== true){
  554. $this->error($hitpay_return['msg']);
  555. }
  556. //支付请求id
  557. $pay_order['payment_request_id'] = $hitpay_return['id'];
  558. $pay_order_id = Db::name('pay_order')->insertGetId($pay_order);
  559. if(!$pay_order_id){
  560. Db::rollback();
  561. $this->error('下单失败');
  562. }
  563. Db::commit();
  564. $return = [
  565. 'url' => $hitpay_return['url'],
  566. 'id' => $hitpay_return['id'],
  567. 'returntype' => 2,
  568. ];
  569. $this->success(1,$return);
  570. }
  571. elseif($paytype == 3)
  572. {
  573. //预约单写入
  574. $lesson_order['order_amount'] = 0;
  575. $lesson_order_id = Db::name('lesson_order')->insertGetId($lesson_order);
  576. if(!$lesson_order_id){
  577. Db::rollback();
  578. $this->error('预约失败');
  579. }
  580. Db::commit();
  581. //去购买套餐,参数代入过去
  582. $return = [
  583. 'lesson_order_id' => $lesson_order_id,
  584. 'returntype' => 3,
  585. ];
  586. $this->success(1,$return);
  587. }
  588. elseif($paytype == 4)
  589. {
  590. //使用试课单
  591. $map = [
  592. 'id' => $trylessonorder_id,
  593. 'user_id' =>$this->auth->id,
  594. 'endtime' => ['gt',time()],
  595. 'order_status' => 10,
  596. ];
  597. $trylesson_order = Db::name('trylesson_order')->where($map)->where('find_in_set(:lesson_ids,lesson_ids)', ['lesson_ids' => $info['lesson_id']])->lock(true)->find();
  598. if(!$trylesson_order){
  599. Db::rollback();
  600. $this->error('试课信息不正确,请刷新重试');
  601. }
  602. //修改预约单数据
  603. $lesson_order['order_amount'] = 0;
  604. $lesson_order['order_status'] = 10;
  605. $lesson_order['paytime'] = time();
  606. $lesson_order['trylesson_order_id'] = $trylessonorder_id;
  607. //预约单写入
  608. $lesson_order_id = Db::name('lesson_order')->insertGetId($lesson_order);
  609. if(!$lesson_order_id){
  610. Db::rollback();
  611. $this->error('预约失败');
  612. }
  613. //改掉试课单状态
  614. $update = [
  615. 'order_status' => 20,
  616. 'updatetime' => time(),
  617. 'lesson_order_id' => $lesson_order_id,
  618. ];
  619. $rs1 = Db::name('trylesson_order')->where('id',$trylessonorder_id)->update($update);
  620. if($rs1 === false){
  621. Db::rollback();
  622. $this->error('使用试课失败');
  623. }
  624. //更新已预约人数
  625. $pay_number = Db::name('lesson_order')->where('slot_id',$slot_id)->where('order_status',10)->sum('usernumber');
  626. $rs_slot = Db::name('lesson_slot')->where('id',$slot_id)->update(['bookednum' => $pay_number]);
  627. if($rs_slot === false){
  628. Db::rollback();
  629. $this->error('预约失败');
  630. }
  631. Db::commit();
  632. $this->success('预约成功',['returntype'=>4]);
  633. }
  634. elseif($paytype == 5)
  635. {
  636. //检查可用的套餐,找一个就可以
  637. $map = [
  638. 'user_id' =>$this->auth->id,
  639. 'endtime' => ['gt',time()],
  640. 'remain' => ['gt',$number_hours],
  641. 'order_status' => 1,
  642. 'use_status' => 1, //已激活的
  643. ];
  644. $package_order = Db::name('package_order')->where($map)->where('find_in_set(:lesson_ids,lesson_ids)', ['lesson_ids' => $info['lesson_id']])->find();
  645. if(!$package_order){
  646. Db::rollback();
  647. $this->error('没有找到足够预约人数的配套,请刷新重试');
  648. }
  649. //扣除一节。延迟到转正的时候处理
  650. /*$update = [
  651. 'remain' => bcsub($package_order['remain'],$number_hours,1),
  652. 'updatetime' => time(),
  653. ];
  654. $rs1 = Db::name('package_order')->where('id',$packageorder_id)->update($update);
  655. if($rs1 === false){
  656. Db::rollback();
  657. $this->error('扣除套餐余额失败');
  658. }*/
  659. //修改预约单数据。延迟到转正的时候处理
  660. /*$lesson_order['order_amount'] = 0;
  661. $lesson_order['order_status'] = 10;
  662. $lesson_order['paytime'] = time();
  663. $lesson_order['package_order_id'] = $packageorder_id;
  664. $lesson_order['package_remark'] = ($package_order['sessions'] - $package_order['remain']) . '-' . ($package_order['sessions'] - $package_order['remain'] + $number_hours) .'/'. $package_order['sessions'];*/
  665. //预约单写入
  666. $lesson_order_id = Db::name('lesson_order')->insertGetId($lesson_order);
  667. if(!$lesson_order_id){
  668. Db::rollback();
  669. $this->error('预约失败');
  670. }
  671. //更新已预约人数。候补就不需要了
  672. /*$pay_number = Db::name('lesson_order')->where('slot_id',$slot_id)->where('order_status',10)->sum('usernumber');
  673. $rs_slot = Db::name('lesson_slot')->where('id',$slot_id)->update(['bookednum' => $pay_number]);
  674. if($rs_slot === false){
  675. Db::rollback();
  676. $this->error('预约失败');
  677. }*/
  678. Db::commit();
  679. $this->success('预约候补成功',['returntype'=>5]);
  680. }
  681. }
  682. //配套商店 所有套餐列表
  683. public function package_shop(){
  684. $where2 = [];
  685. //排除限购一次的
  686. $map = [
  687. 'o.user_id' => $this->auth->id,
  688. 'o.order_status' => 1,
  689. 'o.is_gift' => 0,
  690. 'p.buytimes' => 1,
  691. ];
  692. $check_first = Db::name('package_order')->alias('o')
  693. ->join('lesson_package p','o.package_id = p.id','LEFT')
  694. ->where($map)->column('o.package_id');
  695. if(!empty($check_first)){
  696. $where2['id'] = ['NOTIN',$check_first];
  697. }
  698. //排除新旧会员群体
  699. if($this->auth->oldstatus == 0){ //新会员
  700. $where2['newuser'] = 1;
  701. }
  702. if($this->auth->oldstatus == 1){ //旧会员
  703. $where2['olduser'] = 1;
  704. }
  705. //套餐列表
  706. $list = Db::name('lesson_package')
  707. ->where('is_show',1)
  708. ->where($where2)
  709. ->order('weigh desc')->select();
  710. $list = list_domain_image($list,['image']);
  711. $list = $this->list_lang($list,['name','validity','activeremark']);
  712. $this->success(1,$list);
  713. }
  714. //从预约过来的 售课套餐列表
  715. public function package_list(){
  716. $lesson_id = input('lesson_id',0);
  717. $where2 = [];
  718. //排除限购一次的
  719. $map = [
  720. 'o.user_id' => $this->auth->id,
  721. 'o.order_status' => 1,
  722. 'o.is_gift' => 0,
  723. 'p.buytimes' => 1,
  724. ];
  725. $check_first = Db::name('package_order')->alias('o')
  726. ->join('lesson_package p','o.package_id = p.id','LEFT')
  727. ->where($map)->column('o.package_id');
  728. if(!empty($check_first)){
  729. $where2['id'] = ['NOTIN',$check_first];
  730. }
  731. //排除新旧会员群体
  732. if($this->auth->oldstatus == 0){ //新会员
  733. $where2['newuser'] = 1;
  734. }
  735. if($this->auth->oldstatus == 1){ //旧会员
  736. $where2['olduser'] = 1;
  737. }
  738. //套餐列表
  739. $list = Db::name('lesson_package')
  740. ->where('find_in_set(:lesson_ids,lesson_ids)', ['lesson_ids' => $lesson_id])
  741. ->where('is_show',1)
  742. ->where($where2)
  743. ->order('weigh desc')->select();
  744. $list = list_domain_image($list,['image']);
  745. $list = $this->list_lang($list,['name','validity','activeremark']);
  746. $this->success(1,$list);
  747. }
  748. //售课套餐详情
  749. public function package_info(){
  750. $package_id = input('package_id',0);
  751. //套餐详情
  752. $list = Db::name('lesson_package')
  753. ->where('id',$package_id)
  754. ->order('weigh desc')->find();
  755. $list = info_domain_image($list,['image']);
  756. $list = $this->info_lang($list,['name','validity','activeremark','content']);
  757. $this->success(1,$list);
  758. }
  759. //下单购买售课套餐
  760. public function package_buy(){
  761. $package_id = input('package_id',0);
  762. $lesson_order_id = input('lesson_order_id',0,'intval');//预约单id
  763. $package_info = Db::name('lesson_package')->where('id',$package_id)->find();
  764. //如果限购,则报错
  765. if($package_info['buytimes'] == 1){
  766. $map = [
  767. 'user_id' => $this->auth->id,
  768. 'order_status' => 1,
  769. 'is_gift' => 0,
  770. 'package_id' => $package_id,
  771. ];
  772. $check_first = Db::name('package_order')->where($map)->find();
  773. if(!empty($check_first)){
  774. $this->error('该配套限购一次,您已经买过了');
  775. }
  776. }
  777. //排除新旧会员群体
  778. if($this->auth->oldstatus == 0 && $package_info['newuser'] != 1 ){ //新会员
  779. $this->error('您不能购买改配套,请刷新并重试');
  780. }
  781. if($this->auth->oldstatus == 1 && $package_info['olduser'] != 1 ){ //旧会员
  782. $this->error('您不能购买改配套,请刷新并重试');
  783. }
  784. //套餐订单
  785. $data = [
  786. 'order_no' => createUniqueNo('P',$this->auth->id),
  787. 'user_id' => $this->auth->id,
  788. 'type' => $package_info['type'],
  789. 'package_id' => $package_id,
  790. 'lesson_ids' => $package_info['lesson_ids'],
  791. 'sessions' => $package_info['sessions'],
  792. 'starttime' => 0,
  793. 'days' => $package_info['days'],
  794. 'endtime' => 0,
  795. 'price' => $package_info['price'],
  796. 'remain' => $package_info['sessions'],
  797. 'order_status'=> 0,
  798. 'use_status' => 0,//默认不激活
  799. 'paytime' => 0,
  800. 'pay_type' => 1, //线上支付
  801. 'createtime' => time(),
  802. 'updatetime' => 0,
  803. 'is_gift' => 0,
  804. ];
  805. //如果有赠品,赠品订单
  806. $gift = [];
  807. if(!empty($package_info['gift_lesson_id']) && !empty($package_info['gift_sessions'])){
  808. $gift = $data;
  809. //订单号不换了
  810. $gift['type'] = $package_info['gift_type'];
  811. $gift['lesson_ids'] = $package_info['gift_lesson_id'];
  812. $gift['sessions'] = $package_info['gift_sessions'];
  813. $gift['remain'] = $package_info['gift_sessions'];
  814. $gift['is_gift'] = 1;
  815. }
  816. //支付订单下单
  817. $pay_order = [];
  818. $pay_order['user_id'] = $data['user_id'];
  819. $pay_order['out_trade_no'] = $data['order_no'];
  820. $pay_order['order_amount'] = $data['price'];
  821. $pay_order['createtime'] = $data['createtime'];
  822. $pay_order['pay_type'] = 'hitpay';
  823. $pay_order['platform'] = 'app';
  824. $pay_order['order_status'] = 0;
  825. $pay_order['table_name'] = 'package_order';
  826. $pay_order['table_id'] = 0;
  827. $pay_order['args'] = json_encode(['lesson_order_id'=>$lesson_order_id]);//用来支付完成回调时,扣除N节课,并改掉预约单的状态
  828. //拉起支付
  829. $notify_url = config('notify_cdnurl');
  830. $redirect_cdnurl = config('hitpay.package_redirect_cdnurl').'?out_trade_no='.$pay_order['out_trade_no'].'&lang='.$this->lang;
  831. $hitpay_return = $this->hitpay_payment($pay_order['out_trade_no'],$pay_order['order_amount'],$notify_url,$redirect_cdnurl);
  832. if($hitpay_return['status'] !== true){
  833. $this->error($hitpay_return['msg']);
  834. }
  835. //支付请求id
  836. $pay_order['payment_request_id'] = $hitpay_return['id'];
  837. //入库
  838. Db::startTrans();
  839. $order_id = Db::name('package_order')->insertGetId($data);
  840. if(!$order_id){
  841. Db::rollback();
  842. $this->error('下单失败');
  843. }
  844. //礼物
  845. if(!empty($gift)){
  846. $gift_order_id = Db::name('package_order')->insertGetId($gift);
  847. if(!$gift_order_id){
  848. Db::rollback();
  849. $this->error('下单失败');
  850. }
  851. }
  852. //支付单
  853. $pay_order['table_id'] = $order_id;
  854. $pay_order_id = Db::name('pay_order')->insertGetId($pay_order);
  855. if(!$pay_order_id){
  856. Db::rollback();
  857. $this->error('下单失败');
  858. }
  859. Db::commit();
  860. $return = [
  861. 'url' => $hitpay_return['url'],
  862. 'id' => $hitpay_return['id'],
  863. ];
  864. $this->success(1,$return);
  865. }
  866. //拉起支付
  867. private function hitpay_payment($out_trade_no,$money,$notifyurl,$redirect_cdnurl)
  868. {
  869. $return = [
  870. 'status' => false,
  871. 'msg' => '',
  872. 'url' => '',
  873. 'id' => 0,
  874. ];
  875. $apiKey = config('hitpay.apikey');
  876. try {
  877. $hitPayClient = new \HitPay\Client($apiKey, true);
  878. $request = new \HitPay\Request\CreatePayment();
  879. $request->setAmount($money)
  880. ->setCurrency('SGD')
  881. ->setRedirectUrl($redirect_cdnurl)
  882. // ->setPaymentMethods(['paynow_online','card'])
  883. ->setPaymentMethods(['paynow_online'])
  884. ->setPurpose('Elin Dance Studio')
  885. ->setWebhook($notifyurl)
  886. ->setReferenceNumber($out_trade_no);
  887. $result = $hitPayClient->createPayment($request);
  888. $return['status'] = true;
  889. $return['url'] = $result->getUrl();
  890. $return['id'] = $result->getId();
  891. //print_r($result);
  892. /*$data = $hitPayClient->getPaymentStatus($result->getId());
  893. dump($data);
  894. dump($data->status);*/
  895. /*$data = $hitPayClient->deletePaymentRequest($data->getId());
  896. print_r($data);*/
  897. } catch (\Exception $e) {
  898. $return['msg'] = $e->getMessage();
  899. }
  900. return $return;
  901. }
  902. }