فهرست منبع

feat: 优化剧集

xiansin 2 سال پیش
والد
کامیت
06fe1dda24

+ 3 - 4
mini/api/episode.js

xqd xqd
@@ -18,10 +18,9 @@ export function rank() {
   )
 }
 
-export function info(id) {
+export function detail(id) {
   return request.get(
-    'episode/info',
-    { id }
+    `episode/${id}/detail`
   )
 }
 
@@ -35,6 +34,6 @@ export default {
   recommend,
   news,
   rank,
-  info,
+  detail,
   trace
 }

+ 23 - 2
mini/api/user/collect.js

xqd
@@ -6,11 +6,32 @@ const request = uni.$u.http
 
 export function record(params) {
   return request.get(
-    'user/collect/record',
+    `user/collect/record`,
     { params }
   )
 }
 
+export function check(id) {
+  return request.post(
+    `user/collect/${id}/check`
+  )
+}
+
+export function add(id) {
+  return request.post(
+    `user/collect/${id}/add`
+  )
+}
+
+export function destroy(id) {
+  return request.post(
+    `user/collect/${id}/destroy`
+  )
+}
+
 export default {
-  record
+  record,
+  check,
+  add,
+  destroy
 }

+ 9 - 1
mini/api/user/episode.js

xqd
@@ -16,7 +16,15 @@ export function recent() {
   )
 }
 
+export function watched(id, list_id) {
+  return request.post(
+    'user/watch/episode',
+    { id, list_id }
+  )
+}
+
 export default {
   recent,
-  record
+  record,
+  watched
 }

+ 29 - 0
mini/api/user/favorite.js

xqd
@@ -0,0 +1,29 @@
+
+/**
+ * Created by JianJia.Zhou<jianjia.zhou> on 2022/8/22.
+ */
+const request = uni.$u.http
+
+export function check(id) {
+  return request.post(
+    `user/favorite/${id}/check`
+  )
+}
+
+export function add(id) {
+  return request.post(
+    `user/favorite/${id}/add`
+  )
+}
+
+export function destroy(id) {
+  return request.post(
+    `user/favorite/${id}/destroy`
+  )
+}
+
+export default {
+  check,
+  add,
+  destroy
+}

+ 3 - 1
mini/api/user/index.js

xqd xqd
@@ -9,6 +9,7 @@ import episode from './episode'
 import consume from './consume'
 import recharge from './recharge'
 import collect from './collect'
+import favorite from './favorite'
 
 export async function login() {
   return new Promise(resolve => {
@@ -57,5 +58,6 @@ export default {
   episode,
   consume,
   recharge,
-  collect
+  collect,
+  favorite
 }

+ 210 - 60
mini/pages/episode/play.vue

xqd xqd xqd xqd xqd xqd
@@ -1,61 +1,90 @@
 <template>
   <view class="play-container">
-    <!--播放数据-->
-    <view class="view-num main-left cross-center">
-      <u-icon name="eye-fill" :color="$colors.primaryColor" size="26rpx" />
-      <text>321</text>
-    </view>
-    <!--按钮-->
-    <view class="status-bar dir-top-wrap main-center">
-      <view class="item fav dir-top-wrap main-center cross-center">
-        <u-icon name="heart-fill" size="58rpx" :color="$colors.primaryColor" />
-        <text class="active">3.2k</text>
-      </view>
-      <view class="item collect dir-top-wrap main-center cross-center">
-        <u-icon name="star-fill" size="58rpx" :color="$colors.defaultColor" />
-        <text>3.2k</text>
+    <u-loading-page
+      :loading="loading"
+      :bg-color="$colors.bgColor"
+      :color="$colors.primaryColor"
+      :loading-color="$colors.primaryColor"
+    />
+    <template v-if="!loading">
+      <!--播放数据-->
+      <view class="view-num main-left cross-center">
+        <u-icon name="eye-fill" :color="isCollect?$colors.primaryColor:$colors.defaultColor" size="26rpx" />
+        <text>{{ episode.user_watch_record_count }}</text>
       </view>
+      <!--按钮-->
+      <view class="status-bar dir-top-wrap main-center">
+        <view
+          class="item fav dir-top-wrap main-center cross-center"
+          :class="{active: isFav}"
+          @click="handleFavorite"
+        >
+          <u-icon name="heart-fill" size="58rpx" :color="isFav?$colors.primaryColor:$colors.defaultColor" />
+          <text>{{ episode.user_favorite_count }}</text>
+        </view>
+        <view
+          class="item collect dir-top-wrap main-center cross-center"
+          :class="{active: isCollect}"
+          @click="handleCollect"
+        >
+          <u-icon name="star-fill" size="58rpx" :color="isCollect?$colors.primaryColor:$colors.defaultColor" />
+          <text>{{ episode.user_collect_count }}</text>
+        </view>
 
-      <view class="item share dir-top-wrap main-center cross-center">
-        <u-icon name="share-fill" size="58rpx" :color="$colors.defaultColor" />
-        <text>3.2k</text>
-      </view>
-    </view>
-    <!--底部-->
-    <view class="footer main-between cross-center">
-      <view class="icon" />
-      <view class="name">
-        <u-text text="jldjklsjgkldjglkdfhgklfdhgkldhgjklfhjkfhgjkfdhgjkdfhgk" :color="$colors.infoColor" :lines="1" />
-      </view>
-      <view class="arrow">
-        <u-icon name="arrow-up" :color="$colors.infoColor" size="32rpx" />
+        <view class="item share dir-top-wrap main-center cross-center">
+          <button open-type="share" />
+          <u-icon name="share-fill" size="58rpx" :color="$colors.defaultColor" />
+          <text>{{ episode.sahre_count }}</text>
+        </view>
       </view>
-    </view>
-    <!--视频播放-->
-    <view class="video-box main-center cross-center">
-      <!-- 控制按钮 - 播放 -->
-      <view v-if="!isPlaying" class="play-btn main-center cross-center" @tap="handlePlay">
-        <u-icon name="play-right-fill" size="100rpx" :color="$colors.defaultColor" />
+      <!--底部-->
+      <view class="footer main-between cross-center">
+        <view class="icon" />
+        <view class="name">
+          <u-text :text="episode.name" :color="$colors.infoColor" :lines="1" />
+        </view>
+        <view class="arrow">
+          <u-icon name="arrow-up" :color="$colors.infoColor" size="32rpx" />
+        </view>
       </view>
-      <!-- 控制按钮 - 暂停 -->
-      <view v-if="isPlaying" class="pause-layer" @tap="handlePause" />
-      <!--进度条-->
-      <view v-if="isPlaying" class="progress-container">
-        <view class="progress" :style="{width: progress+'%'}" />
+      <!--视频播放-->
+      <view class="video-box main-center cross-center">
+        <!-- 控制按钮 - 播放 -->
+        <view v-if="!isPlaying" class="play-layer main-center cross-center" @tap="handlePlay">
+          <u-icon name="play-right-fill" size="100rpx" :color="$colors.defaultColor" />
+        </view>
+        <!-- 控制按钮 - 暂停 -->
+        <view v-if="isPlaying" class="pause-layer" @tap="handlePause" />
+        <!--进度条-->
+        <view v-if="isPlaying" class="progress-container">
+          <view class="progress" :style="{width: progress+'%'}" />
+        </view>
+        <video
+          id="video"
+          :show-play-btn="playBtn"
+          :show-fullscreen-btn="fullscreenBtn"
+          :controls="controls"
+          object-fit="contain"
+          style="width: 100%;height: 100%;"
+          :poster="episode.cover_img"
+          :src="src"
+          @timeupdate="timeupdate"
+        />
       </view>
-      <video
-        id="video"
-        :show-play-btn="playBtn"
-        :show-fullscreen-btn="fullscreenBtn"
-        :controls="controls"
-        object-fit="contain"
-        style="width: 100%;height: 100%;"
-        poster="http://zhengda.oss-cn-chengdu.aliyuncs.com/zhangsiye/images/17ec18c234d9f808da0a83cd46749b32.jpg"
-        src="http://zhengda.oss-cn-chengdu.aliyuncs.com/zhangsiye/files/002fa937509b428ca12c826c28aba7c4.mp4"
-        @timeupdate="timeupdate"
+    </template>
+    <!--toast-->
+    <view
+      v-if="toast.show"
+      class="toast dir-top-wrap main-center cross-center"
+      :class="toast.status"
+    >
+      <u-icon
+        :name="toast.status === 'success' ? 'checkmark-circle' : 'close-circle'"
+        :color="toast.status === 'success' ? $colors.primaryColor : $colors.defaultColor"
+        size="80rpx"
       />
+      <text>{{ toast.text }}</text>
     </view>
-
   </view>
 </template>
 
@@ -65,23 +94,48 @@ export default {
   data() {
     return {
       id: null,
+      listId: null,
       isPlaying: false,
       videoContext: null,
       controls: false,
       fullscreenBtn: false,
       playBtn: false,
-      progress: 0
+      progress: 0,
+      episode: null,
+      activeIndex: 0,
+      loading: true,
+      isCollect: false,
+      isFav: false,
+      toast: {
+        status: 'success',
+        text: '收藏成功',
+        show: false
+      }
+    }
+  },
+  computed: {
+    src() {
+      if (this.episode) {
+        return this.episode.lists[this.activeIndex].url
+      }
+    }
+  },
+  watch: {
+    'toast.show'(val) {
+      console.log('-->data1', val)
+      if (val) {
+        setTimeout(() => {
+          this.toast.show = false
+        }, 1000)
+      }
     }
   },
-  computed: {},
   methods: {
     timeupdate({ detail }) {
       // currentTime, duration
       if (detail.duration) {
         this.progress = (detail.currentTime / detail.duration * 100).toFixed(2)
       }
-      console.log('-->data', detail)
-      // this.progress =
     },
     handlePlay() {
       this.isPlaying = true
@@ -91,11 +145,73 @@ export default {
       if (!this.isPlaying) return
       this.isPlaying = false
       this.videoContext.pause()
+    },
+    getEpisode() {
+      this.loading = true
+      this.$api.episode.detail(this.id).then(res => {
+        this.loading = false
+        this.episode = res.data
+        if (this.listId) {
+          this.episode.lists.forEach((obj, index) => {
+            if (parseInt(this.listId) === obj.id) {
+              this.activeIndex = index
+            }
+          })
+        } else {
+          this.listId = this.episode.lists[0].id
+        }
+        this.watched(this.id, this.listId)
+      })
+    },
+    checkCollect() {
+      this.$api.user.collect.check(this.id).then(res => {
+        this.isCollect = res.data
+      })
+    },
+    handleCollect() {
+      const method = this.isCollect ? 'destroy' : 'add'
+      const num = this.isCollect ? -1 : 1
+      this.$api.user.collect[method](this.id).then(res => {
+        if (res.data) {
+          this.toast.show = true
+          this.toast.status = this.isCollect ? 'cancel' : 'success'
+          this.toast.text = this.isCollect ? '取消收藏' : '收藏成功'
+          this.episode.user_collect_count += num
+          this.isCollect = !this.isCollect
+        }
+      })
+    },
+    checkFavorite() {
+      this.$api.user.favorite.check(this.id).then(res => {
+        this.isFav = res.data
+      })
+    },
+    handleFavorite() {
+      const method = this.isFav ? 'destroy' : 'add'
+      const num = this.isFav ? -1 : 1
+      this.$api.user.favorite[method](this.id).then(res => {
+        if (res.data) {
+          this.toast.show = true
+          this.toast.status = this.isFav ? 'cancel' : 'success'
+          this.toast.text = this.isFav ? '取消喜欢' : '喜欢成功'
+          this.episode.user_favorite_count += num
+          this.isFav = !this.isFav
+        }
+      })
+    },
+    watched(id, list_id) {
+      this.$api.user.episode.watched(id, list_id).then(res => {
+
+      })
     }
   },
   onLoad(options) {
     this.videoContext = uni.createVideoContext('video', this)
     this.id = options.id
+    this.listId = options?.list_Id
+    this.getEpisode()
+    this.checkCollect()
+    this.checkFavorite()
   }
 }
 </script>
@@ -109,14 +225,14 @@ export default {
         left: 0;
         right: 0;
         bottom: 0;
-        .play-btn{
-          position: absolute;
+        .play-layer{
+          position: fixed;
           top: 0;
           left: 0;
           bottom: 0;
           right: 0;
-          background: rgba(255,255,255,0);
-          z-index: 99;
+          background: transparent;
+          z-index: 999;
         }
         .pause-layer{
           position: absolute;
@@ -164,11 +280,26 @@ export default {
         z-index: 100;
         .item{
           margin-bottom: 40rpx;
+          &.share{
+            position: relative;
+            button{
+              position: absolute;
+              background: transparent;
+              top: 0;
+              left: 0;
+              right: 0;
+              bottom: 0;
+              z-index: 1;
+              &:after{
+                content: unset;
+              }
+            }
+          }
+          &.active{
+            color: $primary-color;
+          }
           text{
             margin-top: 10rpx;
-            &.active{
-              color: $primary-color;
-            }
           }
         }
       }
@@ -198,5 +329,24 @@ export default {
           padding: 0 30rpx;
         }
       }
+
+      .toast{
+        position: fixed;
+        width: 60vw;
+        background: rgba(0,0,0,.5);
+        height: 300rpx;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%,-50%);
+        border-radius: 20rpx;
+        font-size: 36rpx;
+        color: $default-color;
+        &.success{
+          color: $primary-color;
+        }
+        text{
+          margin-top: 20rpx;
+        }
+      }
     }
 </style>

+ 10 - 1
mini/pages/index/components/Recent.vue

xqd xqd
@@ -14,7 +14,7 @@
       </view>
       <view class="recent">上次观看至 <text>第{{ recent.detail.sort }}集</text></view>
     </view>
-    <view class="play-btn">继续观看</view>
+    <view class="play-btn" @click="handlePlay(recent)">继续观看</view>
   </view>
 </template>
 
@@ -31,6 +31,15 @@ export default {
     this.getRecent()
   },
   methods: {
+    handlePlay(item) {
+      this.$u.route({
+        url: '/pages/episode/play',
+        params: {
+          id: item.episode_id,
+          list_id: item.list_id
+        }
+      })
+    },
     getRecent() {
       this.$api.user.episode.recent().then(res => {
         if (res.data) {

+ 40 - 9
mini/pages/trace/collect.vue

xqd xqd
@@ -6,23 +6,24 @@
       class="item main-left"
     >
       <view class="cover-image">
-        <image src="/static/image/default-movie.png" />
+        <image :src="item.episode.cover_img" />
       </view>
       <view class="right-box">
         <view class="op-group main-right cross-center">
-          <view class="delete">删除</view>
-          <view class="play">播放</view>
+          <view class="delete" @click="handleDelete(item)">删除</view>
+          <view class="play" @click="handlePlay(item)">播放</view>
         </view>
-        <view class="name">毒液</view>
+        <view class="name">{{ item.episode.name }}</view>
         <view class="status-box">
-          <text class="status">已完结</text>
-          <text>共18集</text>
+          <text class="status">{{ item.episode.status_text }}</text>
+          <text>共{{ item.episode.total }}集</text>
         </view>
         <view class="record">
-          上次看至 <text>第集</text>
+          上次看至 <text>第{{ item.watch_record.detail.sort }}集</text>
         </view>
       </view>
     </view>
+    <!--删除  收藏-->
   </view>
 </template>
 
@@ -30,11 +31,41 @@
 export default {
   data() {
     return {
-      collect: [1, 2, 3, 4, 5]
+      collect: [],
+      modal: {
+        show: false,
+        item: {}
+      }
+
     }
   },
   computed: {},
-  methods: {}
+  methods: {
+    handlePlay(item) {
+      this.$u.route({
+        url: '/pages/episode/play',
+        params: {
+          id: item.episode.id,
+          list_id: item.watch_record.list_id
+        }
+      })
+    },
+    handleDelete(item) {
+    },
+    destroy() {
+
+    },
+    getCollect() {
+      this.$loading()
+      this.$api.user.collect.record({ limit: 4 }).then(res => {
+        this.$hideLoading()
+        this.collect = res.data
+      })
+    }
+  },
+  onLoad() {
+    this.getCollect()
+  }
 }
 </script>
 

+ 4 - 4
server/app/Admin/Controllers/Episode/EpisodeController.php

xqd xqd
@@ -27,7 +27,7 @@ class EpisodeController extends AdminController
             $grid->column('category.name','分类');
             $grid->column('platform')->using(config('global.platform'));
             $grid->column('name')->editable();
-            $grid->column('episodes_price')->editable();
+            //$grid->column('episodes_price')->editable();
             $grid->column('sale_count','已售出')->display(function () {
                 return $this->userEpisodesRecords->count();
             })->label('primary');
@@ -109,9 +109,9 @@ class EpisodeController extends AdminController
             $form->switch('is_opend')->default(1);
             $form->number('sort');
             $form->switch('is_vip_watch')->default(1);
-            $form->number('free_episodes')->required();
-            $form->number('paid_episodes')->required();
-            $form->number('episodes_price')->required();
+//            $form->number('free_episodes')->required();
+//            $form->number('paid_episodes')->required();
+//            $form->number('episodes_price')->required();
 
             $form->display('created_at');
             $form->display('updated_at');

+ 9 - 0
server/app/Admin/Controllers/Episode/EpisodesListController.php

xqd xqd
@@ -70,6 +70,10 @@ class EpisodesListController extends AdminController
             $grid->column('sort')->display(function (){
                 return '第'.$this->sort.'集';
             })->editable();
+            $grid->column('is_free')
+                ->using(config('global.episode_free'))
+                ->label(['success', 'primary'])->sortable();
+            $grid->column('sale_price')->editable();
             $grid->column('url')->display(function (){
                 return '<video src="'.$this->url.'" width="200" controls></vedio>';
             });
@@ -111,6 +115,11 @@ class EpisodesListController extends AdminController
             $form->display('id');
             $form->hidden('episodes_id')->value($this->episode->id);
             $form->number('sort')->min(1);
+            $form->radio('is_free')
+                ->options(config('global.episode_free'))
+                ->when(1, function (Form $form) {
+                    $form->number('sale_price');
+                })->default(1);
             $form->file('url')->chunkSize(1024)->maxSize(1024 * 1024)->saveFullUrl()
                 ->uniqueName()->autoUpload()
                 ->autoSave(false)

+ 11 - 0
server/app/Http/Controllers/V1/EpisodeController.php

xqd
@@ -114,4 +114,15 @@ class EpisodeController extends Controller
 
         return $this->success($lists);
     }
+
+    public function detail($id)
+    {
+        $episodes = Episode::withCount(['userWatchRecord','userCollect','userFavorite','lists'])
+            ->with(['category:id,name','lists:id,episodes_id,sort,url'])
+            ->where('is_opend', 1)
+            ->where('id', $id)
+            ->where('platform', \user()->info->platform)
+            ->first();
+        return $this->success($episodes);
+    }
 }

+ 1 - 424
server/app/Http/Controllers/V1/PayController.php

xqd
@@ -1,432 +1,9 @@
 <?php
 
-namespace App\Http\Controllers\Api\V1;
+namespace App\Http\Controllers\V1;
 
-use Illuminate\Support\Facades\Log;
-use Omnipay\Omnipay;
-use App\Helper\LogHelper;
-use App\Helper\PayHelper;
-use App\Helper\JpushHelper;
-use App\Models\UserInfoModel;
-use App\Models\OrderInfoModel;
-use App\Models\AccountLog;
-use App\Services\Base\ErrorCode;
-use Illuminate\Http\Request;
-use Config, Auth, DB ,Validator;
 
 class PayController extends Controller
 {
-    use PayHelper, LogHelper,JpushHelper;
 
-    public function alipayNotify() {
-        $rawInfo = Request::all();
-        \Log::info('aliplay callback');
-        \Log::info($rawInfo);
-
-        $config = Config::get('laravel-omnipay.gateways.alipay');
-
-        $gateway = Omnipay::create($config['driver']);
-        $gateway->setEnvironment($config['options']['environment']);
-        $gateway->setAppId($config['options']['appid']);
-        $gateway->setEncryptKey($config['options']['encrypt_key']);
-        $gateway->setPrivateKey($config['options']['prikey']);
-        $gateway->setAlipayPublicKey($config['options']['ali_pubkey']);
-        $gateway->setNotifyUrl($config['options']['notify_url']);
-
-        $request = $gateway->completePurchase();
-        $request->setParams($rawInfo);//Optional
-
-        try {
-            $response = $request->send();
-
-            if($response->isPaid()){
-                \Log::info('支付成功');
-
-                $out_trade_no = $rawInfo['out_trade_no'];
-                $order = Order::where('transaction_id', '=', $out_trade_no)->first();
-                if (!$order) {
-                    \Log::error('找不到订单' . $out_trade_no);
-                    return 'fail';
-                }
-                $master_amount =  $order->number;
-                $slave_amount = 0;
-                if (!empty($order->ext_info)) {
-                    $extInfo = json_decode($order->ext_info, true);
-                    if ($extInfo !== null) {
-                        $cc = $extInfo['cc_bonus'];
-                        $master_amount = round(($cc / 100) * $order->number);
-                        $slave_amount = $order->number - $master_amount;
-                    }
-                }
-                $tp = '';
-//                $u = null;
-                if ($order->user_type == Order::USER_TYPE_MERCHANT) {
-                    $tp = 'Merchant';
-//                    $u = Merchant::find($order->user_id);
-                } elseif ($order->user_type == Order::USER_TYPE_MEMBER) {
-                    $tp = 'Member';
-                    //Not handled here
-                }
-                $u = UserInfoModel::find($order->user_id);
-                if (!$u) {
-                    \Log::error('用户不存在' . $order->user_type . ', ' . $order->user_id);
-                    return 'success';
-                }
-
-                DB::beginTransaction();
-
-                if ($order->goods_type == Order::GOODS_TYPE_BALANCE||$order->goods_type == Order::GOODS_TYPE_COSUME) {
-                    $cType = AccountLog::TYPE_BALANCE;
-//                    $u->account_balance += $order->number;
-                    $u->balance += $master_amount;
-                } elseif ($order->goods_type == Order::GOODS_TYPE_CREDIT) {
-                    $cType = AccountLog::TYPE_CREDIT;
-//                    $u->credit_balance += $order->number;
-                    $u->credit += $master_amount;
-                } else {
-                    \Log::error('商品不存在' . $order->goods_type);
-                    return 'success';
-                }
-                \Log::info('支付金额 '.$master_amount);
-                if ($u->save()) {
-
-                    //更新订单状态
-                    $order->status = Order::STATUS_FINISHED;
-                    $order->save();
-
-                    //记日志
-                    $amount = $rawInfo['total_amount'] * 100;
-                    $_ops = AccountLog::getAllop();
-                    $this->logAccount($tp, $u->id, $u->name,
-                            AccountLog::OP_CHARGE, $cType, $master_amount,
-                            AccountLog::DIRECTION_INC, $cType == AccountLog::TYPE_BALANCE ? $u->balance : $u->credit, AccountLog::CHANNEL_ALIPAY);
-
-                    \Log::info('支付完成');
-
-                } else {
-                    DB::rollBack();
-                    \Log::error('保存数据失败');
-                }
-
-                DB::commit();
-
-                return 'success';
-            }else{
-                \Log::error('支付失败');
-
-                return 'fail';
-            }
-        } catch (\Exception $e) {
-            \Log::error('支付异常' . $e->getMessage());
-
-            return 'fail';
-        }
-
-    }
-
-    public function wechatpayNotify() {
-        \Log::debug('wechatpay callback');
-//        \Log::debug(Request::all());
-
-        $config = Config::get('laravel-omnipay.gateways.wechatpay');
-
-        $gateway = Omnipay::create("WechatPay");
-//        $gateway->setEnvironment('sandbox');
-        $gateway->setAppId($config['options']['appid']);
-        $gateway->setMchId($config['options']['merchant_id']);
-        $gateway->setApiKey($config['options']['apikey']);
-
-        $response = $gateway->completePurchase([
-            'request_params' => file_get_contents('php://input')
-        ])->send();
-        try {
-            if ($response->isPaid()) {
-                //pay success
-                $rawInfo = $response->getRequestData();
-                \Log::debug($rawInfo);
-                \Log::info('支付成功');
-
-                $out_trade_no = $rawInfo['out_trade_no'];
-                $order = OrderInfoModel::where('transaction_id', '=', $out_trade_no)->first();
-                if (!$order) {
-                    \Log::error('找不到订单' . $out_trade_no);
-                    return 'fail';
-                }
-                $u = UserInfoModel::find($order->user_id);
-                if (!$u) {
-                    \Log::error('用户不存在' . $order->user_id . ', ' . $order->user_id);
-                    return 'success';
-                }
-
-                DB::beginTransaction();
-                $u->coin += $order->number;
-                if ($u->save()) {
-
-                    //更新订单状态
-                    $order->status = OrderInfoModel::STATUS_FINISHED;
-                    $order->save();
-
-                    //记日志
-                    $_types = AccountLog::getAllType();
-                    $_ops = AccountLog::getAllop();
-                    $this->logAccount($_types[AccountLog::TYPE_CASH], $u->id, $u->name,$order->amount,
-                        $_ops[AccountLog::OP_CHARGE],
-                        $_types[AccountLog::TYPE_COIN], $u->id,$u->name,$order->amount,$out_trade_no,'http://miao.beiyuesi.com/base/img/wechat.png');
-
-                    \Log::info('支付完成');
-
-                } else {
-                    DB::rollBack();
-                    \Log::error('保存数据失败');
-                }
-
-                DB::commit();
-
-                return 'success';
-            }else{
-                //pay fail
-                \Log::error($response->getData());
-                return 'fail';
-            }
-
-        } catch (\Exception $e) {
-            \Log::error('支付异常' . $e->getMessage());
-
-            return 'fail';
-        }
-    }
-
-
-
-    /**
-     * @api {post} /api/pay/charge 充值
-     * @apiDescription 充值(向平台充值)
-     * @apiGroup User
-     * @apiPermission Passport
-     * @apiVersion 0.1.0
-     * @apiParam {int}  [goods=1]   充值商品(1:梦想币)
-     * @apiParam {int}  number      充值数量,人民币,以分表示
-     * @apiParam {int}  type        支付类型(1:支付宝,2:微信支付)
-     * @apiSuccessExample {json} Success-Response:
-     * HTTP/1.1 200 OK
-     * {
-     *     "state": true,
-     *     "code": 0,
-     *     "message": "",
-     *     "data": {
-     *         "id": 2,
-     *         "code": "ALIPAY_201610231314145719",
-     *         "transaction_id": "201610231314145719",
-     *         "user_type": 2,
-     *         "user_id": 1,
-     *         "goods_type": 1,
-     *         "price": 1,
-     *         "number": 1,
-     *         "amount": 1,
-     *         "pay_type": 1,
-     *         "status": 0,
-     *         "created_at": "2016-10-23 13:14:14",
-     *         "updated_at": "2016-10-23 13:14:14",
-     *         "orderString": "alipay_sdk=lokielse%2Fomnipay-alipay&app_id=2016091201894867&biz_content=%7B%22subject%22%3A%22%5Cu7532%5Cu8c61%5Cu8054%5Cu5408-%5Cu4f59%5Cu989d%5Cu5145%5Cu503c%22%2C%22out_trade_no%22%3A%22201610231314145719%22%2C%22total_amount%22%3A0.01%2C%22product_code%22%3A1%7D&charset=UTF-8&format=JSON&method=alipay.trade.app.pay&notify_url=http%3A%2F%2Fweb%2Fapi%2Fpay%2Falipay%2Fnotify&sign_type=RSA&timestamp=2016-10-23+13%3A14%3A14&version=1.0&sign=oxKM0qGMHLWDlMrXHIiy9%2Fk2BXJq3rC3RKdmcfFwkBJVRXvtG6cBoAYPll6VxJYOMQWeu78Ibfov%2FxIVCuN9yUfzEiokfQrzBoptc94bCQ5k0pNyJcSdgezOUKHB12P5Zmm3Hd6AAbGRDV9UCaLVz0wYkFJPrCyUv1ZfhrM%2BBqc%3D"
-     *     }
-     * }
-     * @apiErrorExample {json} Error-Response:
-     * HTTP/1.1 400 Bad Request
-     * {
-     *     "state": false,
-     *     "code": 1000,
-     *     "message": "传入参数不正确",
-     *     "data": null or []
-     * }
-     * 可能出现的错误代码:
-     * 1000    CLIENT_WRONG_PARAMS             传入参数不正确
-     */
-    public function charge(Request $request)
-    {
-        $goodsTypes = OrderInfoModel::getAllGoodsTypes();
-        $gTypes = array_keys($goodsTypes);
-        $payTypes = OrderInfoModel::getAllPayTypes();
-        $pTypes = array_keys($payTypes);
-        $validator = Validator::make($data = $request->all(),
-            [
-                'number' => 'required|integer|min:1',
-                'type' => 'required|in:' . join(',', $pTypes),
-                'goods' => 'in:' . join(',', $gTypes),
-            ],
-            [
-                'number.required' => '请输入梦想币数量',
-                'number.integer' => '梦想币数量必须为整数',
-                'number.min' => '充值梦想币数量至少为1',
-                'type.required' => '支付类型必填',
-                'type.in' => '支付类型非法',
-                'goods.in' => '充值商品非法',
-            ]
-        );
-
-        if ($validator->fails()) {
-            return $this->validatorError($validator->messages()->all(), ErrorCode::CLIENT_WRONG_PARAMS);
-        }
-
-        $user = Auth::guard('api')->user();
-        $data['goods_id'] = 1;
-        if (!isset($data['goods'])) {//默认充余额
-            $data['goods'] = OrderInfoModel::GOODS_TYPE_COIN;
-        }
-        $data['user_id'] = $user->id;
-        if ($data['type'] == OrderInfoModel::PAY_TYPE_ALIPAY) {
-            $result = $this->createAlipayCharge($data);
-        } else {
-            $result = $this->createWechatpayCharge($data);
-        }
-
-        if ($result === false) {
-            return $this->error(ErrorCode::ORDER_GENERATE_FAILED);
-        }
-        //log
-//        $data['amount'] = $data['number'];
-//        $this->chargeLog('Merchant', $merchant->id, $merchant->name,
-//            'balance', $data['amount'], 'balance', $data['amount'],
-//            'Merchant', $user->id, $merchant->name);
-
-        return $this->api($result);
-    }
-
-
-
-    /**
-     * @api {post} /api/pay/order_status 订单状态查询
-     * @apiDescription 订单状态查询
-     * @apiGroup User
-     * @apiPermission Passport
-     * @apiVersion 0.1.0
-     * @apiParam {int}  [goods=1]   充值商品(1:梦想币)
-     * @apiParam {int}  number      充值数量,人民币,以分表示
-     * @apiParam {int}  type        支付类型(1:支付宝,2:微信支付)
-     * @apiSuccessExample {json} Success-Response:
-     * HTTP/1.1 200 OK
-     * {
-     *     "state": true,
-     *     "code": 0,
-     *     "message": "",
-     *     "data": {
-     *         "id": 2,
-     *         "code": "ALIPAY_201610231314145719",
-     *         "transaction_id": "201610231314145719",
-     *         "user_type": 2,
-     *         "user_id": 1,
-     *         "goods_type": 1,
-     *         "price": 1,
-     *         "number": 1,
-     *         "amount": 1,
-     *         "pay_type": 1,
-     *         "status": 0,
-     *         "created_at": "2016-10-23 13:14:14",
-     *         "updated_at": "2016-10-23 13:14:14",
-     *         "orderString": "alipay_sdk=lokielse%2Fomnipay-alipay&app_id=2016091201894867&biz_content=%7B%22subject%22%3A%22%5Cu7532%5Cu8c61%5Cu8054%5Cu5408-%5Cu4f59%5Cu989d%5Cu5145%5Cu503c%22%2C%22out_trade_no%22%3A%22201610231314145719%22%2C%22total_amount%22%3A0.01%2C%22product_code%22%3A1%7D&charset=UTF-8&format=JSON&method=alipay.trade.app.pay&notify_url=http%3A%2F%2Fweb%2Fapi%2Fpay%2Falipay%2Fnotify&sign_type=RSA&timestamp=2016-10-23+13%3A14%3A14&version=1.0&sign=oxKM0qGMHLWDlMrXHIiy9%2Fk2BXJq3rC3RKdmcfFwkBJVRXvtG6cBoAYPll6VxJYOMQWeu78Ibfov%2FxIVCuN9yUfzEiokfQrzBoptc94bCQ5k0pNyJcSdgezOUKHB12P5Zmm3Hd6AAbGRDV9UCaLVz0wYkFJPrCyUv1ZfhrM%2BBqc%3D"
-     *     }
-     * }
-     * @apiErrorExample {json} Error-Response:
-     * HTTP/1.1 400 Bad Request
-     * {
-     *     "state": false,
-     *     "code": 1000,
-     *     "message": "传入参数不正确",
-     *     "data": null or []
-     * }
-     * 可能出现的错误代码:
-     * 1000    CLIENT_WRONG_PARAMS             传入参数不正确
-     */
-
-    public function orderStatus(Request $request)
-    {
-
-        $validator = Validator::make($data = $request->all(),
-            [
-                'number' => 'required|integer|min:1',
-                'type' => 'required|in:' . join(',', $pTypes),
-                'goods' => 'in:' . join(',', $gTypes),
-            ],
-            [
-                'number.required' => '请输入梦想币数量',
-                'number.integer' => '梦想币数量必须为整数',
-                'number.min' => '充值梦想币数量至少为1',
-                'type.required' => '支付类型必填',
-                'type.in' => '支付类型非法',
-                'goods.in' => '充值商品非法',
-            ]
-        );
-
-        if ($validator->fails()) {
-            return $this->validatorError($validator->messages()->all(), ErrorCode::CLIENT_WRONG_PARAMS);
-        }
-        $order = new OrderInfoModel();
-        $order->code            = $data['code'];
-        $order->transaction_id  = $data['transaction_id'];
-        $order->user_id         = $data['user_id'];
-        $order->goods_id        = $data['goods_id'];
-        $order->price           = $data['price'];
-        $order->number          = $data['number'];
-        $order->amount          = $data['amount'];
-        $order->pay_type        = $data['pay_type'];
-        if (isset($data['ext_info'])) {
-            $order->ext_info = $data['ext_info'];
-        }
-
-        if ($order->save()) {
-            return $order->find($order->id);
-        }
-        return $this->api('');
-    }
-
-    public function orderIos(Request $request)
-    {
-
-        $validator = Validator::make($data = $request->all(),
-            [
-                'number' => 'required',
-            ],
-            [
-                'number.required' => '请输入产品ID',
-            ]
-        );
-
-        if ($validator->fails()) {
-            return $this->validatorError($validator->messages()->all(), ErrorCode::CLIENT_WRONG_PARAMS);
-        }
-//        $order = new OrderInfoModel();
-
-        Log::info($data['number']);
-
-        $AppleProducts = OrderInfoModel::getAllAppleProducts();
-        $coin = 0 ;
-        if(isset($AppleProducts[$data['number']])){
-            $coin = $AppleProducts[$data['number']];
-        }
-//        $order->code            = $data['code'];
-//        $order->transaction_id  = $data['transaction_id'];
-//        $order->user_id         = $data['user_id'];
-//        $order->goods_id        = $data['goods_id'];
-//        $order->price           = $data['price'];
-//        $order->number          = $data['number'];
-//        $order->amount          = $data['amount'];
-//        $order->pay_type        = $data['pay_type'];
-
-        //更新订单状态
-//        $order->status = OrderInfoModel::STATUS_FINISHED;
-//        $order->save();
-
-        $u = Auth::guard('api')->user();
-        $u->coin += $coin;
-        $u->save();
-        //记日志
-        $_types = AccountLog::getAllType();
-        $_ops = AccountLog::getAllop();
-        $this->logAccount($_types[AccountLog::TYPE_CASH], $u->id, $u->name,$coin,
-            $_ops[AccountLog::OP_CHARGE],
-            $_types[AccountLog::TYPE_COIN], $u->id,$u->name,$coin,$data['number'],'http://miao.beiyuesi.com/base/img/apple.png');
-
-        \Log::info('支付完成');
-
-        return $this->api('');
-    }
 }

+ 27 - 7
server/app/Http/Controllers/V1/User/CollectController.php

xqd xqd
@@ -12,13 +12,18 @@ use Illuminate\Database\Eloquent\Builder;
 
 class CollectController extends Controller
 {
+    // 收藏记录
     public function record(Request $request)
     {
         $limit = $request->input('limit', 10);
+        $page = $request->input('page', 1);
+        $offset = ($page - 1) * 10;
         $lists  = UserCollect::filterUser()
             ->with(['episode','watchRecord.detail'])
+            ->orderByDesc('id')
             ->limit($limit)
-            ->orderByDesc('id')->get();
+            ->offset($offset)
+            ->get();
 
         foreach ($lists as $list){
             $count = $list->episode->withCount('lists')->first()->toArray();
@@ -29,18 +34,33 @@ class CollectController extends Controller
         return $this->success($lists);
     }
 
-    public function check()
+    // 检查当前剧集是否收藏
+    public function check($id)
     {
-        
+        $res = UserCollect::filterUser()->where('episode_id',$id)->count();
+        return $this->success(!empty($res));
     }
 
-    public function add()
+    // 添加收藏
+    public function add($id)
     {
-        
+        $res = UserCollect::filterUser()->where('episode_id',$id)->count();
+        if($res){
+            return $this->success(true);
+        }
+        $collect = new UserCollect();
+        $collect->user_id = \user()->id;
+        $collect->episode_id = $id;
+        return $this->success($collect->save());
     }
 
-    public function destory()
+    // 删除收藏
+    public function destroy($id)
     {
-
+        $res = UserCollect::filterUser()->where('episode_id',$id)->first();
+        if(!$res){
+            return $this->success(false);
+        }
+        return $this->success($res->delete());
     }
 }

+ 46 - 0
server/app/Http/Controllers/V1/User/FavoriteController.php

xqd
@@ -0,0 +1,46 @@
+<?php
+namespace App\Http\Controllers\V1\User;
+
+use App\Http\Controllers\V1\Controller;
+use App\Models\UserCollect;
+use App\Models\UserConsumeRecord;
+use App\Models\UserEpisodesRecord;
+use App\Models\UserFavorite;
+use App\Models\UserWatchRecord;
+use Carbon\Carbon;
+use Dingo\Api\Http\Request;
+use Illuminate\Database\Eloquent\Builder;
+
+class FavoriteController extends Controller
+{
+
+    // 检查当前剧集是否喜欢
+    public function check($id)
+    {
+        $res = UserFavorite::filterUser()->where('episode_id',$id)->count();
+        return $this->success(!empty($res));
+    }
+
+    // 添加喜欢
+    public function add($id)
+    {
+        $res = UserFavorite::filterUser()->where('episode_id',$id)->count();
+        if($res){
+            return $this->success(true);
+        }
+        $collect = new UserFavorite();
+        $collect->user_id = \user()->id;
+        $collect->episode_id = $id;
+        return $this->success($collect->save());
+    }
+
+    // 删除喜欢
+    public function destroy($id)
+    {
+        $res = UserFavorite::filterUser()->where('episode_id',$id)->first();
+        if(!$res){
+            return $this->success(false);
+        }
+        return $this->success($res->delete());
+    }
+}

+ 19 - 0
server/app/Http/Controllers/V1/User/WatchRecordsController.php

xqd xqd
@@ -2,6 +2,7 @@
 namespace App\Http\Controllers\V1\User;
 
 use App\Http\Controllers\V1\Controller;
+use App\Models\User;
 use App\Models\UserWatchRecord;
 
 class WatchRecordsController extends Controller
@@ -33,4 +34,22 @@ class WatchRecordsController extends Controller
 
         return $this->success($info);
     }
+
+    public function watched()
+    {
+        $id = request()->input('id');
+        $listId = request()->input('list_id');
+        $res = UserWatchRecord::filterUser()->where('episode_id',$id)->first();
+        if($res){
+            $res->list_id = $listId;
+        }else{
+            $res = new UserWatchRecord();
+            $res->user_id = \user()->id;
+            $res->episode_id = $id;
+        }
+
+        $res->list_id = $listId;
+
+        return $this->success($res->save());
+    }
 }

+ 27 - 3
server/app/Models/Episode.php

xqd xqd xqd xqd
@@ -26,7 +26,7 @@ use Illuminate\Database\Eloquent\Model;
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property-read \App\Models\EpisodesCategory|null $category
  * @property-read \App\Models\EpisodesList|null $lists
- *@property-read int|null $lists_count
+ * @property-read int|null $lists_count
  * @method static \Illuminate\Database\Eloquent\Builder|Episode newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder|Episode newQuery()
  * @method static \Illuminate\Database\Query\Builder|Episode onlyTrashed()
@@ -53,6 +53,11 @@ use Illuminate\Database\Eloquent\Model;
  * @method static \Illuminate\Database\Eloquent\Builder|Episode wherePlatform($value)
  * @property-write mixed $status_text
  * @property-write mixed $total
+ * @property-read \App\Models\UserCollect|null $userCollect
+ * @property-read \App\Models\UserFavorite|null $userFavorite
+ * @property int $share_count 分享数量
+ * @method static \Illuminate\Database\Eloquent\Builder|Episode whereShareCount($value)
+ * @property-read \App\Models\UserWatchRecord|null $userWatchRecord
  */
 class Episode extends Model
 {
@@ -69,7 +74,7 @@ class Episode extends Model
 
     public function lists()
     {
-        return $this->belongsTo(EpisodesList::class,'id','episodes_id');
+        return $this->hasMany(EpisodesList::class,'episodes_id','id');
     }
 
     public function setStatusTextAttribute($value)
@@ -83,8 +88,27 @@ class Episode extends Model
         $this->attributes['total'] = $value;
     }
 
+    /* 购买记录 */
     public function userEpisodesRecords()
     {
-        return $this->belongsTo(UserEpisodesRecord::class,'id','episodes_id');
+        return $this->hasMany(UserEpisodesRecord::class,'episodes_id','id');
+    }
+
+    // 收藏
+    public function userCollect()
+    {
+        return $this->belongsTo(UserCollect::class,'id','episode_id');
+    }
+
+    // 喜欢
+    public function userFavorite()
+    {
+        return $this->belongsTo(UserFavorite::class,'id','episode_id');
+    }
+
+    // 观看记录
+    public function userWatchRecord()
+    {
+        return $this->belongsTo(UserWatchRecord::class,'id','episode_id');
     }
 }

+ 4 - 0
server/app/Models/EpisodesList.php

xqd
@@ -39,6 +39,10 @@ class EpisodesList extends Model
 
     protected $table = 'episodes_lists';
 
+    protected $hidden = [
+        'updated_at','deleted_at'
+    ];
+
     public function episode()
     {
         return $this->belongsTo(Episode::class,'episodes_id','id');

+ 3 - 1
server/app/Models/UserFavorite.php

xqd xqd
@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Scopes\UserScope;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\SoftDeletes;
@@ -29,10 +30,11 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static \Illuminate\Database\Query\Builder|UserFavorite withoutTrashed()
  * @mixin \Eloquent
  * @property-read \App\Models\Episode|null $episode
+ * @method static \Illuminate\Database\Eloquent\Builder|UserFavorite filterUser()
  */
 class UserFavorite extends Model
 {
-    use HasFactory,SoftDeletes;
+    use HasFactory,SoftDeletes,UserScope;
 
     protected $table = 'user_favorite';
 

+ 2 - 0
server/config/global.php

xqd
@@ -7,6 +7,8 @@ return [
     'platform' => [1 => '抖音', 2 => '快手'],
     // 剧集状态
     'episode_status' => ['持续更新中','已完结'],
+    // 剧集免费
+    'episode_free' => ['免费','付费'],
     // 剧集上架状态
     'episode_opend' => ['已下架','销售中'],
     // 是否状态

+ 3 - 1
server/resources/lang/zh/episodes-list.php

xqd
@@ -8,7 +8,9 @@ return [
     ],
     'fields' => [
         'episodes_id' => '剧集ID',
-        'sort' => '排序',
+        'sort' => '第几集',
+        'is_free' => '是否免费',
+        'sale_price' => '价格',
         'url' => '视频链接',
     ],
     'options' => [

+ 12 - 0
server/routes/api.php

xqd xqd xqd
@@ -80,6 +80,7 @@ $api->version('v1', ['namespace' => 'App\Http\Controllers\V1'], function ($api)
                 /* @var Dingo\Api\Routing\Router $api*/
                 $api->get('record', 'WatchRecordsController@lists');
                 $api->get('recent', 'WatchRecordsController@recent');
+                $api->post('episode', 'WatchRecordsController@watched');
             });
 
             $api->group(['prefix' => 'consume','namespace' => 'User'], function ($api){
@@ -95,6 +96,16 @@ $api->version('v1', ['namespace' => 'App\Http\Controllers\V1'], function ($api)
             $api->group(['prefix' => 'collect','namespace' => 'User'], function ($api){
                 /* @var Dingo\Api\Routing\Router $api*/
                 $api->get('record', 'CollectController@record');
+                $api->post('{id}/check', 'CollectController@check');
+                $api->post('{id}/add', 'CollectController@add');
+                $api->post('{id}/destroy', 'CollectController@destroy');
+            });
+
+            $api->group(['prefix' => 'favorite','namespace' => 'User'], function ($api){
+                /* @var Dingo\Api\Routing\Router $api*/
+                $api->post('{id}/check', 'FavoriteController@check');
+                $api->post('{id}/add', 'FavoriteController@add');
+                $api->post('{id}/destroy', 'FavoriteController@destroy');
             });
         });
 
@@ -114,6 +125,7 @@ $api->version('v1', ['namespace' => 'App\Http\Controllers\V1'], function ($api)
             $api->get('news', 'EpisodeController@news'); // 最新
             $api->get('rank', 'EpisodeController@rank'); // 排行
             $api->get('trace', 'EpisodeController@trace'); // 追剧
+            $api->get('{id}/detail', 'EpisodeController@detail'); // 详情
         });
 
     });