
'use strict';

const Service = require('egg').Service;
const R = require('ramda');
const _ = require('lodash');
const moment = require('moment');
const sequelize = require('sequelize');

class InstitutionSubService extends Service {
  // 获取分类
  async getCats() {
    const { ctx } = this;

    const AllCats = await ctx.classModel.V4.CourseCat.findAll({ where: { status: 1, is_deleted: 0 } });
    const rootCats = [];
    let ret = [];

    // 先取出一级分类
    for (const v of AllCats) {
      if (v.parent_id === 0) {
        rootCats[v.id] = v.dataValues;
        rootCats[v.id].child = [{ id: 0, value: v.id, name: '全部', parent_id: 0, image: 'http://r.51gjj.com/webpublic/images/20191118/s6yRUsc5kclyu.png', active_image: 'http://r.51gjj.com/webpublic/images/20191118/zYHkYp85vxk5m.png', color: '', tips: '', status: 'online', is_deleted: 0 }];
      }
    }

    // 放入二级分类
    for (const v of AllCats) {
      if (v.parent_id > 0) {
        v.value = v.id;
        rootCats[v.parent_id].child.push(v);
      }
    }

    const sort = function(a, b) {
      return a.sort - b.sort;
    };

    // 整理
    for (const v of rootCats) {
      if (!ctx.isEmpty(v)) {
        v.child = R.sort(sort)(v.child);
        ret.push(v);
      }
    }

    ret = R.sort(sort)(ret);

    return { results: ret };
  }


  // 获取机构详情
  async getInstitution(input) {
    const { ctx } = this;
    const userUuid = ctx.userUuid;
    const { institution_id } = input;
    const lat = ctx.isEmpty(input.lat) ? 0 : input.lat;
    const lng = ctx.isEmpty(input.lng) ? 0 : input.lng;

    let institution = await ctx.classModel.V4.CourseInstitution.findOne({ where: { id: institution_id, status: 'online', is_deleted: 0 } });
    if (ctx.isEmpty(institution)) {
      ctx.failed('机构不存在');
    }
    institution = institution.dataValues;

    // 顶部相册
    const images = await ctx.classModel.V4.CourseImages.findAll({ where: { type: 1, type_id: institution_id, status: 'online', is_deleted: 0 } });
    // 去重
    const checkList = [];
    let album = [];
    for (const v of images) {
      if ((v.is_video === 0 && checkList.includes(v.image_url)) || (v.is_video === 1 && checkList.includes(v.video_url))) {
        continue;
      }
      album.push(v);
    }

    // 排序，视频在前
    const albumSort = function(a, b) {
      if (a.is_video === b.is_video) {
        return a.sort - b.sort;
      }
      return b.is_video - a.is_video;
    };
    album = R.sort(albumSort)(album);

    // 学习成果
    const student_video = await ctx.classModel.V4.CourseStudentVideo.findAll({ where: { institution_id, status: 'online', is_deleted: 0 }, order: [[ 'sort', 'asc' ]] });

    // 教师
    const teachers = await ctx.classModel.V4.CourseTeacher.findAll({ where: { institution_id, status: 'online', is_deleted: 0 }, raw: true });

    // 课程
    const classes = await ctx.classModel.V4.CourseClass.findAll({ where: { institution_id, status: 'online', is_deleted: 0 }, raw: true });
    // 处理课程封面图
    const classHandle = [];
    if (classes.length > 0) {
      for (const v of classes) {
        classHandle.push(ctx.classModel.V4.CourseImages.findOne({ where: { type: 2, type_id: v.id, status: 'online', is_deleted: 0 }, order: [[ 'sort', 'asc' ]] }));
      }
      const classImages = await Promise.all(classHandle).then(result => {
        return result;
      }).catch(error => {
        ctx.failed(error);
      });

      for (const i in classes) {
        classes[i].image = classImages[i];
      }
    }

    // 校区
    let areas = await ctx.classModel.V4.CourseArea.findAll({ where: { institution_id, status: 'online', is_deleted: 0 }, raw: true });
    const areaHandle = [];
    if (areas.length > 0) {
      for (const v of areas) {
        areaHandle.push(this.formatArea(v, { lng, lat }));
      }
      areas = await Promise.all(areaHandle).then(result => {
        return result;
      }).catch(error => {
        ctx.failed(error);
      });
    }
    areas = _.orderBy(areas, [ 'distance' ], [ 'asc' ]);

    // 是否已收藏
    const userCollect = await ctx.classModel.V4.CourseUserCollection.findOne({ where: { type: 1, type_id: institution_id, user_uuid: userUuid, is_deleted: 0 } });

    // 获取所有标签
    const allTags = await ctx.classModel.V4.CourseTag.findAll({ where: { status: 'online', is_deleted: 0 } });
    const tagList = [];
    for (const v of allTags) {
      tagList[v.id] = v;
    }

    let tags = await ctx.classModel.V4.CourseInstitutionToTag.findAll({ where: { institution_id: institution.id, status: 'online', is_deleted: 0 } });
    tags = _.orderBy(tags, [ 'sort' ], [ 'asc' ]);
    institution.tags = [];
    for (const v of tags) {
      institution.tags.push(tagList[v.tag_id]);
    }

    institution.phone = areas.length > 0 ? areas[0].phone : '';
    institution.distance = areas.length > 0 ? String(areas[0].distance) + 'km' : '无法计算';
    institution.travel_method = areas.length > 0 ? areas[0].travel_method : '';
    institution.travel_tips = areas.length > 0 ? areas[0].travel_tips : '';

    const ret = {
      album,
      detail: institution,
      student_video,
      teachers,
      classes,
      areas,
      is_collected: ctx.isEmpty(userCollect) ? 0 : 1,
    };

    return ret;
  }

  // 处理校区
  async formatArea(area, location) {
    const { ctx, service } = this;
    if (ctx.isEmpty(area)) {
      return {};
    }

    const distance = await service.course.v4.lbs.getDistance({ lng: location.lng, lat: location.lat }, { lng: area.lng, lat: area.lat });
    // 暂定3公里以内步行
    area.travel_method = distance < 3000 ? 'walking' : 'driving';
    const lbsResult = await service.course.v4.lbs.getLBSDistance(area.travel_method, { lng: location.lng, lat: location.lat }, [{ lng: area.lng, lat: area.lat }]);
    if (lbsResult.results.length > 0) {
      area.distance = parseFloat((lbsResult.results[0].distance / 1000).toFixed(1));
      area.duration = lbsResult.results[0].duration;
      const minute = area.travel_method === 'walking' ? Math.ceil(area.distance / 0.08) : Math.ceil(area.duration / 60);
      area.travel_tips = area.travel_method === 'walking' ? `距我${area.distance}km,步行${minute}分钟` : `距我${area.distance}km,开车${minute}分钟`;
    } else {
      area.distance = 999999;
      area.duration = 0;
      area.travel_tips = '';
    }

    return area;
  }

  // 机构列表
  async getInstitutions(input) {
    const { ctx } = this;
    const userUuid = ctx.userUuid;
    const { cat_id, age, address } = input;
    const lat = ctx.isEmpty(input.lat) ? 0 : input.lat;
    const lng = ctx.isEmpty(input.lng) ? 0 : input.lng;

    // 保存定位记录
    if (address && lat && lng) {
      ctx.classModel.V4.CourseLogUserGps.create({ user_uuid: userUuid, address, lat, lng, created_time: moment().format('YYYY-MM-DD HH:mm:ss') });
    }

    let institutionList = [];
    let institutionCats = [];
    const filter = { where: { status: 'online', is_deleted: 0 } };
    // 年龄筛选
    if (Number(age) > 0) {
      filter.where.min_age = { $lte: age };
      filter.where.max_age = { $gte: age };
    }
    // 分类筛选
    if (Number(cat_id) > 0) {
      // 如果是一级分类，则需要加入该分类下所有子分类
      const cats = await ctx.classModel.V4.CourseCat.findAll({ where: { parent_id: cat_id, status: 'online', is_deleted: 0 } });
      const catIds = R.pluck('id', cats);
      catIds.push(Number(cat_id));
      institutionCats = await ctx.classModel.V4.CourseInstitutionToCat.findAll({ where: { status: 'online', is_deleted: 0, cat_id: { $in: catIds } } });
      const institutionIds = R.pluck('institution_id', institutionCats);
      filter.where.id = { $in: institutionIds };
    }

    institutionList = await ctx.classModel.V4.CourseInstitution.findAll(filter);
    institutionList = R.pluck('dataValues', institutionList);


    const ret = await this.formatInstitutionList(institutionList, input);
    return ret;
  }


  // 格式化机构列表
  async formatInstitutionList(institutionList, input) {
    const { ctx, service } = this;
    const userUuid = ctx.userUuid;
    const distance = Number(input.distance) || 0;
    const page = Number(input.page) || 1;
    const limit = Number(input.limit) || 10;
    const { lng, lat } = input;

    let institutionCats = [];
    let institutionIds = R.pluck('id', institutionList);

    // 获取机构对应的校区
    const areaList = await ctx.classModel.V4.CourseArea.findAll({ where: { status: 'online', is_deleted: 0, institution_id: { $in: institutionIds } } });

    // 计算校区对应的距离
    const areaHandle = [];
    for (const v of areaList) {
      areaHandle.push(service.course.v4.lbs.getDistance({ lat, lng }, { lat: v.lat, lng: v.lng }));
    }
    const areaDistanceCalcResult = await Promise.all(areaHandle).then(result => {
      return result;
    }).catch(error => {
      ctx.failed(error);
    });
    for (const i in areaList) {
      areaList[i].distance = areaDistanceCalcResult[i];
    }

    // 选出最短距离的校区
    for (const v of areaList) {
      for (const i in institutionList) {
        if (institutionList[i].id === v.institution_id) {
          if (ctx.isEmpty(institutionList[i].area) || (institutionList[i].area.distance > v.distance)) {
            institutionList[i].area = v;
          }
        }
      }
    }

    // 格式化机构距离
    for (const i in institutionList) {
      institutionList[i].distance = ctx.isEmpty(institutionList[i].area) ? 999999.0 : institutionList[i].area.distance;
    }
    institutionList = _.orderBy(institutionList, [ 'distance' ], [ 'asc' ]);

    // 距离筛选
    if (Number(distance) > 0) {
      for (const i in institutionList) {
        if (institutionList[i].distance > Number(distance)) {
          institutionList = institutionList.slice(0, i);
          break;
        }
      }
    }

    // 分页
    const ret = {
      results: [],
      count: institutionList.length,
    };
    institutionList = institutionList.slice(Number(page - 1) * Number(limit), Number(page) * Number(limit));
    institutionIds = R.pluck('id', institutionList);


    // 获取所有分类
    const allCats = await ctx.classModel.V4.CourseCat.findAll({ where: { status: 'online', is_deleted: 0 } });
    const catList = [];
    for (const v of allCats) {
      catList[v.id] = v;
    }
    institutionCats = await ctx.classModel.V4.CourseInstitutionToCat.findAll({ where: { status: 'online', is_deleted: 0, institution_id: { $in: institutionIds } } });

    // 获取所有标签
    const allTags = await ctx.classModel.V4.CourseTag.findAll({ where: { status: 'online', is_deleted: 0 } });
    const tagList = [];
    for (const v of allTags) {
      tagList[v.id] = v;
    }

    // 用户已收藏机构列表
    const userInstitutions = await ctx.classModel.V4.CourseUserCollection.findAll({ where: { type: 1, is_deleted: 0, user_uuid: userUuid } });
    const userInstitutionIds = R.pluck('type_id', userInstitutions);

    // 机构图片及格式化
    for (const i in institutionList) {
      // 格式化机构分类（展示一级分类）
      let cats = [];
      for (const v of institutionCats) {
        if (v.institution_id === institutionList[i].id && catList[v.cat_id]) {
          if (catList[v.cat_id].parent_id === 0) {
            cats.push(v.cat_id);
          } else {
            cats.push(catList[v.cat_id].parent_id);
          }
        }
      }
      // 去重
      cats = _.uniq(cats);
      institutionList[i].cats = [];
      for (const v of cats) {
        institutionList[i].cats.push(catList[v]);
      }

      // 标签
      let tags = await ctx.classModel.V4.CourseInstitutionToTag.findAll({ where: { institution_id: institutionList[i].id, status: 'online', is_deleted: 0 } });
      tags = _.orderBy(tags, [ 'sort' ], [ 'asc' ]);
      institutionList[i].tags = [];
      for (const v of tags) {
        institutionList[i].tags.push(tagList[v.tag_id]);
      }

      // 优先获取机构详情图
      let institutionImages = await ctx.classModel.V4.CourseImages.findAll({ where: { type: 1, type_id: institutionList[i].id, status: 'online', is_deleted: 0 }, order: [[ 'sort', 'asc' ]], limit: 3 });
      if (institutionImages.length < 3) {
        const defaultImages = await ctx.classModel.V4.CourseImages.findAll({ where: { type: 4, type_id: { $in: cats }, status: 'online', is_deleted: 0 }, limit: 3 - institutionImages.length });
        institutionImages = institutionImages.concat(defaultImages);
      }
      institutionList[i].images = institutionImages;

      institutionList[i].distance = String((institutionList[i].distance / 1000).toFixed(1)) + 'km';
      institutionList[i].phone = institutionList[i].area.length > 0 ? institutionList[i].area[0].phone : '';

      // 是否已收藏
      institutionList[i].is_collected = userInstitutionIds.includes(institutionList[i].id) ? 1 : 0;
      ret.results.push(institutionList[i]);
    }

    return ret;
  }


  // 搜索
  async search(input) {
    const { ctx } = this;
    const { cat_id, age, search } = input;
    const userUuid = ctx.userUuid;

    // 保存搜索记录
    if (search) {
      const searchInfo = await ctx.classModel.V4.CourseSearch.findOne({ where: { content: search, is_deleted: 0 } });
      if (searchInfo) {
        await ctx.classModel.V4.CourseSearch.update({ count: searchInfo.count + 1 }, { where: { id: searchInfo.id } });
      } else {
        await ctx.classModel.V4.CourseSearch.create({ content: search, sort: 0, count: 1, is_hot: 0, status: 'online', is_deleted: 0, created_time: moment().format('YYYY-MM-DD HH:mm:ss'), updated_time: moment().format('YYYY-MM-DD HH:mm:ss') });
      }

      await ctx.classModel.V4.CourseUserSearch.create({ user_uuid: userUuid, content: search, status: 'online', created_time: moment().format('YYYY-MM-DD HH:mm:ss'), updated_time: moment().format('YYYY-MM-DD HH:mm:ss') });
    }

    let institutionList = [];
    let institutionCats = [];
    let institutionIds = [];
    const filter = { where: { status: 'online', is_deleted: 0 }, row: true };

    const classFilter = { where: { status: 'online', is_deleted: 0, name: { $like: '%' + search + '%' } }, attributes: [ 'id', 'institution_id', 'name', 'status', 'is_deleted' ], row: true };
    // 年龄筛选
    if (Number(age) > 0) {
      filter.where.min_age = { $lte: age };
      filter.where.max_age = { $gte: age };
    }
    // 分类筛选
    if (Number(cat_id) > 0) {
      // 如果是一级分类，则需要加入该分类下所有子分类
      const cats = await ctx.classModel.V4.CourseCat.findAll({ where: { parent_id: cat_id, status: 'online', is_deleted: 0 } });
      const catIds = R.pluck('id', cats);
      catIds.push(Number(cat_id));
      institutionCats = await ctx.classModel.V4.CourseInstitutionToCat.findAll({ where: { status: 'online', is_deleted: 0, cat_id: { $in: catIds } } });
      institutionIds = R.pluck('institution_id', institutionCats);
    }

    if (institutionIds.length > 0) {
      filter.where.id = { $in: institutionIds };
      classFilter.where.institution_id = { $in: institutionIds };
    }
    const institutionFilter = filter;
    institutionFilter.where.name = { $like: '%' + search + '%' };
    institutionList = await ctx.classModel.V4.CourseInstitution.findAll(institutionFilter);
    const ids = R.pluck('id', institutionList);

    // 课程搜索
    const classList = await ctx.classModel.V4.CourseClass.findAll(classFilter);
    // 去除已经搜到的机构
    const classInstitutionIds = _.difference(R.pluck('institution_id', classList), ids);
    if (classInstitutionIds.length > 0) {
      filter.where.id = { $in: classInstitutionIds };
      const classInstitutionList = await ctx.classModel.V4.CourseInstitution.findAll(filter);
      institutionList = institutionList.concat(classInstitutionList);
    }

    institutionList = R.pluck('dataValues', institutionList);
    const ret = await this.formatInstitutionList(institutionList, input);
    return ret;
  }


  // 获取热搜
  async getHotSearch() {
    const { ctx } = this;
    const search = await ctx.classModel.V4.CourseSearch.findAll({ where: { status: 'online', is_deleted: 0, is_hot: 1 }, order: [[ 'sort', 'asc' ]] });

    const ret = {
      results: R.pluck('content', search),
      count: search.length,
    };

    return ret;
  }


  // 获取用户搜索历史
  async getUserSearch() {
    const { ctx } = this;
    const userUuid = ctx.userUuid;
    const search = await ctx.classModel.V4.CourseUserSearch.findAll({ where: { status: 'online', user_uuid: userUuid }, limit: 10, order: [[ 'id', 'desc' ]] });
    const result = _.uniq(R.pluck('content', search));

    const ret = {
      results: result,
      count: result.length,
    };

    return ret;
  }


  // 删除用户搜索历史
  async deleteUserSearch() {
    const { ctx } = this;
    const userUuid = ctx.userUuid;
    await ctx.classModel.V4.CourseUserSearch.update({ status: 'offline' }, { where: { user_uuid: userUuid, status: 'online' } });
    return;
  }


  // 评论列表
  async getComments(input) {
    const { ctx } = this;
    const page = Number(input.page) || 1;
    const limit = Number(input.limit) || 10;
    const offset = (page - 1) * limit;
    const institution_id = Number(input.institution_id) || 0;

    const comments = await ctx.classModel.V4.CourseComment.findAndCountAll({ where: { institution_id, status: 'online', is_deleted: 0 }, offset, limit, order: [[ 'id', 'desc' ]] });
    comments.rows = R.pluck('dataValues', comments.rows);

    const ret = {
      results: await this.formatComments(comments.rows),
      count: comments.count,
    };

    return ret;
  }

  // 格式化评论列表
  async formatComments(comments) {
    const { ctx } = this;
    if (ctx.isEmpty(comments)) {
      return [];
    }

    const ids = R.pluck('id', comments);
    const images = await ctx.classModel.V4.CourseImages.findAll({ where: { type: 3, type_id: { $in: ids }, status: 'online', is_deleted: 0 } });

    const ret = [];
    for (const v of comments) {
      // 评论图片
      let commentImages = [];
      for (const j of images) {
        if (j.type_id === v.id) {
          commentImages.push(j);
        }
      }
      commentImages = _.orderBy(commentImages, [ 'sort' ], [ 'asc' ]);

      ret.push({
        id: v.id,
        institution_id: v.institution_id,
        user_uuid: v.user_uuid,
        nickname: v.nickname,
        avatar: v.avatar,
        content: v.content,
        has_image: v.has_image,
        images: commentImages,
        created_time: v.created_time,
      });
    }

    return ret;
  }


  // 搜索关联
  async getSuggestSearch(input) {
    const { ctx } = this;
    const { search } = input;
    let results = [];
    if (!ctx.isEmpty(search)) {
      const institutions = await ctx.classModel.V4.CourseInstitution.findAll({ where: { status: 'online', is_deleted: 0, name: { $like: '%' + search + '%' } }, limit: 10, attributes: [ 'id', 'name', 'status', 'is_deleted' ] });
      results = R.pluck('name', institutions);
    }

    const ret = {
      results,
      count: results.length,
    };

    return ret;
  }


  // 教师详情
  async getTeacher(id) {
    const { ctx } = this;
    let teacher = await ctx.classModel.V4.CourseTeacher.findOne({ where: { id, status: 'online', is_deleted: 0 } });
    if (ctx.isEmpty(teacher)) {
      ctx.failed('数据不存在');
    }

    teacher = teacher.dataValues;
    teacher.point_tags = teacher.point ? teacher.point.split(',') : [];
    teacher.work_experience_tags = teacher.work_experience ? teacher.work_experience.split(';') : [];

    const institution = await ctx.classModel.V4.CourseInstitution.findOne({ where: { id: teacher.institution_id } });
    teacher.institution_name = institution.name;

    return teacher;
  }


  // 教师列表
  async getTeachers(input) {
    const { ctx } = this;
    const page = Number(input.page) || 1;
    const limit = Number(input.limit) || 10;
    const offset = (page - 1) * limit;
    const institutionId = Number(input.institution_id) || 0;
    const teachers = await ctx.classModel.V4.CourseTeacher.findAndCountAll({ where: { institution_id: institutionId, status: 'online', is_deleted: 0 }, raw: true, offset, limit });

    const ret = {
      results: teachers.rows,
      count: teachers.count,
    };
    return ret;
  }


  // 课程列表
  async getClasses(input) {
    const { ctx } = this;
    const page = Number(input.page) || 1;
    const limit = Number(input.limit) || 10;
    const offset = (page - 1) * limit;
    const institutionId = Number(input.institution_id) || 0;
    const inputClassIds = input.classIds || [];
    const attributes = [ 'id', 'institution_id', 'name', 'type', 'class_price', 'min_age', 'max_age', 'status', 'is_deleted' ];
    let classes = { rows: [], count: 0 };
    if (institutionId > 0) {
      classes = await ctx.classModel.V4.CourseClass.findAndCountAll({ where: { institution_id: institutionId, status: 'online', is_deleted: 0 }, raw: true, offset, limit, attributes });
    } else if (inputClassIds.length > 0) {
      classes = await ctx.classModel.V4.CourseClass.findAndCountAll({ where: { id: { $in: inputClassIds }, status: 'online', is_deleted: 0 }, raw: true, offset, limit, attributes });
    }
    const classIds = R.pluck('id', classes.rows);

    // 课程图片
    const classImages = await ctx.classModel.V4.CourseImages.findAll({ where: { type: 2, type_id: { $in: classIds }, status: 'online', is_deleted: 0 }, raw: true });
    // 用户收藏的课程
    const userCollection = await ctx.classModel.V4.CourseUserCollection.findAll({ where: { user_uuid: ctx.userUuid, is_deleted: 0, type: 2 }, raw: true });
    const collectedIds = R.pluck('type_id', userCollection);
    for (const i in classes.rows) {
      let images = [];
      for (const v of classImages) {
        if (v.type_id === classes.rows[i].id) {
          images.push(v);
        }
      }
      images = _.orderBy(images, [ 'sort' ], [ 'asc' ]);
      classes.rows[i].image = images.length > 0 ? images[0] : {};
      classes.rows[i].is_collected = collectedIds.includes(classes.rows[i].id) ? 1 : 0;
      classes.rows[i].age = classes.rows[i].min_age === 0 && classes.rows[i].max_age === 0 ? '' : `${classes.rows[i].min_age}-${classes.rows[i].max_age}岁`;
      classes.rows[i].class_price = classes.rows[i].class_price || '到店咨询';
    }

    const ret = {
      results: classes.rows,
      count: classes.count,
    };
    return ret;
  }


  // 课程详情
  async getClass(input) {
    const { ctx } = this;
    const classInfo = await ctx.classModel.V4.CourseClass.findOne({ where: { id: input.id, status: 'online', is_deleted: 0 }, raw: true });
    if (ctx.isEmpty(classInfo)) {
      ctx.failed('数据不存在');
    }

    // 课程图片
    let images = await ctx.classModel.V4.CourseImages.findAll({ where: { type: 2, type_id: input.id, status: 'online', is_deleted: 0 }, raw: true });
    // 去重
    images = _.uniqBy(images, function(v) { return (v.is_image === 1 ? v.image_url : v.video_url); });
    images = _.orderBy(images, [ 'sort' ], [ 'asc' ]);

    classInfo.images = images;
    classInfo.class_price = ctx.isEmpty(classInfo.class_price) ? '' : classInfo.class_price + '元/课时';
    input.institution_id = classInfo.institution_id;
    const institution = await this.getInstitution(input);
    classInfo.institution = ctx.isEmpty(institution) ? {} : institution.detail;
    classInfo.area = ctx.isEmpty(institution) || ctx.isEmpty(institution.areas) ? {} : institution.areas[0];

    const isCollected = await ctx.classModel.V4.CourseUserCollection.findOne({ where: { user_uuid: ctx.userUuid, type: 2, type_id: input.id, is_deleted: 0 } });
    classInfo.is_collected = ctx.isEmpty(isCollected) ? 0 : 1;
    return classInfo;
  }


  // 用户收藏机构列表
  async getUserCollectedInstitutions(input) {
    const { ctx } = this;
    const userUuid = ctx.userUuid;

    const userCollection = await ctx.classModel.V4.CourseUserCollection.findAll({ where: { user_uuid: userUuid, is_deleted: 0, type: 1 }, raw: true });
    const institutionIds = R.pluck('type_id', userCollection);
    const institutionList = await ctx.classModel.V4.CourseInstitution.findAll({ where: { id: { $in: institutionIds }, status: 'online', is_deleted: 0 }, raw: true });

    const ret = await this.formatInstitutionList(institutionList, input);
    return ret;
  }


  // 用户收藏课程列表
  async getUserCollectedClasses(input) {
    const { ctx } = this;
    const userUuid = ctx.userUuid;

    const userCollection = await ctx.classModel.V4.CourseUserCollection.findAll({ where: { user_uuid: userUuid, is_deleted: 0, type: 2 }, raw: true });
    const classIds = R.pluck('type_id', userCollection);
    input.classIds = classIds;

    const ret = await this.getClasses(input);
    return ret;
  }


  // 获取指定分类及子分类的选课指南
  async getArticlesByCat(input) {
    const { ctx } = this;
    const page = Number(input.page) || 1;
    const limit = Number(input.limit) || 2;
    const offset = (page - 1) * limit;
    const catId = Number(input.cat_id) || 0;
    let catIds = [];
    const filter = { where: { status: 'online', is_deleted: 0 }, order: [[ 'sort', 'asc' ], [ 'id', 'desc' ]], limit, offset };

    // 获取子分类
    if (catId > 0) {
      const cats = await ctx.classModel.V4.CourseCat.findAll({ where: { parent_id: catId, status: 'online', is_deleted: 0 } });
      catIds = R.pluck('id', cats);
      catIds.push(catId);
      filter.where.cat_id = { $in: catIds };
    }

    // 获取分类
    const cats = await ctx.classModel.V4.CourseCat.findAll({ where: { status: 'online', is_deleted: 0 } });
    const catMap = new Map();
    for (const v of cats) {
      catMap.set(v.id, v);
    }

    const articles = await ctx.classModel.V4.CourseArticle.findAndCountAll(filter);

    const ret = [];
    // 用户点赞列表
    const userLikes = await ctx.classModel.V4.CourseLike.findAll({ where: { user_uuid: ctx.userUuid, type: 1, is_deleted: 0 } });
    const userLikeIds = R.pluck('type_id', userLikes);
    for (const v of articles.rows) {
      ret.push({
        id: v.id,
        type: v.type,
        cat_id: v.cat_id,
        cat_title: catMap.has(v.cat_id) ? catMap.get(v.cat_id).name : '',
        title: v.title,
        description: v.description,
        content: v.content,
        image: v.image,
        source: v.source,
        like_count: v.like_count,
        read_count: v.read_count,
        sort: v.sort,
        is_like: userLikeIds.includes(v.id) ? 1 : 0,
        created_time: v.created_time,
      });
    }

    return { results: ret, count: articles.count, page };
  }


  // 获取选课指南
  async getArticle(id) {
    const { ctx } = this;
    const article = await ctx.classModel.V4.CourseArticle.findOne({ where: { id, status: 'online', is_deleted: 0 } });
    if (ctx.isEmpty(article)) {
      ctx.failed('数据不存在');
    }

    await ctx.classModel.V4.CourseArticle.update({ read_count: sequelize.literal('`read_count` + 1') }, { where: { id } });

    // 用户是否点赞
    const userLike = await ctx.classModel.V4.CourseLike.findOne({ where: { user_uuid: ctx.userUuid, type: 1, type_id: id, is_deleted: 0 } });

    // 获取分类
    const cats = await ctx.classModel.V4.CourseCat.findAll({ where: { status: 'online', is_deleted: 0 } });
    const catMap = new Map();
    for (const v of cats) {
      catMap.set(v.id, v);
    }

    // 获取关联列表
    let relations = [];
    if (!ctx.isEmpty(article.relations)) {
      const ids = article.relations.split(',');
      relations = await ctx.classModel.V4.CourseArticle.findAll({ where: { id: { $in: ids }, status: 'online', is_deleted: 0 }, order: [[ 'sort', 'asc' ], [ 'id', 'desc' ]], attributes: [ 'id', 'title', 'type', 'image', 'read_count' ] });
    }
    const ret = {
      id: article.id,
      type: article.type,
      cat_id: article.cat_id,
      cat_title: catMap.has(article.cat_id) ? catMap.get(article.cat_id).name : '',
      title: article.title,
      description: article.description,
      content: article.content,
      image: article.image,
      source: article.source,
      like_count: article.like_count,
      read_count: article.read_count,
      sort: article.sort,
      relations,
      is_like: ctx.isEmpty(userLike) ? 0 : 1,
      created_time: article.created_time,
    };

    return ret;
  }


  // 点赞 1：选课指南；
  async like(input) {
    const { ctx } = this;
    const type = Number(input.type) || 0;
    const typeId = Number(input.type_id) || 0;
    let tmp = {};

    if (type === 0 || typeId === 0) {
      ctx.failed('参数异常');
    }

    // 是否重复点赞
    tmp = await ctx.classModel.V4.CourseLike.findOne({ where: { user_uuid: ctx.userUuid, type, type_id: typeId, is_deleted: 0 } });
    if (!ctx.isEmpty(tmp)) {
      ctx.failed('请勿重复点赞');
    }

    // 校验点赞对象是否存在
    switch (type) {
      case 1:
        tmp = await ctx.classModel.V4.CourseArticle.findOne({ where: { id: typeId, status: 'online', is_deleted: 0 } });
        if (ctx.isEmpty(tmp)) {
          ctx.failed('数据不存在');
        }
        // 更新点赞数
        await ctx.classModel.V4.CourseArticle.update({ like_count: sequelize.literal('`like_count` + 1') }, { where: { id: typeId } });
        break;
      default:
        break;
    }

    await ctx.classModel.V4.CourseLike.create({ user_uuid: ctx.userUuid, type, type_id: typeId, is_deleted: 0, created_time: moment().format('YYYY-MM-DD HH:mm:ss') });
    return;
  }

  // 取消点赞
  async unlike(input) {
    const { ctx } = this;
    const type = Number(input.type) || 0;
    const typeId = Number(input.type_id) || 0;
    let tmp = {};

    if (type === 0 || typeId === 0) {
      ctx.failed('参数异常');
    }

    // 是否重复点赞
    const userLike = await ctx.classModel.V4.CourseLike.findOne({ where: { user_uuid: ctx.userUuid, type, type_id: typeId, is_deleted: 0 } });
    if (ctx.isEmpty(userLike)) {
      ctx.failed('尚未点赞');
    }

    // 校验点赞对象是否存在
    switch (type) {
      case 1:
        tmp = await ctx.classModel.V4.CourseArticle.findOne({ where: { id: typeId, status: 'online', is_deleted: 0 } });
        if (ctx.isEmpty(tmp)) {
          ctx.failed('数据不存在');
        }
        // 更新点赞数
        await ctx.classModel.V4.CourseArticle.update({ like_count: sequelize.literal('`like_count` - 1') }, { where: { id: typeId } });
        break;
      default:
        break;
    }

    await ctx.classModel.V4.CourseLike.update({ is_deleted: 1 }, { where: { id: userLike.id } });
    return;
  }

  // 获取选课指南推荐列表
  async getArticlesByRecommend(input) {
    const { ctx } = this;
    const page = Number(input.page) || 1;
    const limit = Number(input.limit) || 2;
    const offset = (page - 1) * limit;

    const articles = await ctx.classModel.V4.CourseArticle.findAndCountAll({ where: { is_recommend: 1, status: 'online', is_deleted: 0 }, order: [[ 'sort', 'asc' ], [ 'id', 'desc' ]], limit, offset });
    // 获取分类
    const cats = await ctx.classModel.V4.CourseCat.findAll({ where: { status: 'online', is_deleted: 0 } });
    const catMap = new Map();
    for (const v of cats) {
      catMap.set(v.id, v);
    }


    const ret = [];
    // 用户点赞列表
    const userLikes = await ctx.classModel.V4.CourseLike.findAll({ where: { user_uuid: ctx.userUuid, type: 1, is_deleted: 0 } });
    const userLikeIds = R.pluck('type_id', userLikes);
    for (const v of articles.rows) {
      ret.push({
        id: v.id,
        type: v.type,
        cat_id: v.cat_id,
        cat_title: catMap.has(v.cat_id) ? catMap.get(v.cat_id).name : '',
        title: v.title,
        description: v.description,
        content: v.content,
        image: v.image,
        source: v.source,
        like_count: v.like_count,
        read_count: v.read_count,
        sort: v.sort,
        is_like: userLikeIds.includes(v.id) ? 1 : 0,
        created_time: v.created_time,
      });
    }

    return { results: ret, count: articles.count, page };
  }
}

module.exports = InstitutionSubService;
