You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							278 lines
						
					
					
						
							5.9 KiB
						
					
					
				
			
		
		
	
	
							278 lines
						
					
					
						
							5.9 KiB
						
					
					
				| /* eslint-disable camelcase */
 | |
| import httpStatus from 'http-status';
 | |
| import { Model, DataTypes, Op } from 'sequelize';
 | |
| import { isEqual, isNil, isUndefined, omitBy, pick } from 'lodash';
 | |
| import moment from 'moment-timezone';
 | |
| import { serviceName } from '../../config/vars';
 | |
| import postgres from '../../config/postgres';
 | |
| import APIError from '../utils/APIException';
 | |
| 
 | |
| /**
 | |
|  * Create connection
 | |
|  */
 | |
| const { sequelize } = postgres;
 | |
| class File extends Model { }
 | |
| 
 | |
| const PUBLIC_FIELDS = [
 | |
|     'name',
 | |
|     'title',
 | |
|     'payload'
 | |
| ];
 | |
| 
 | |
| /**
 | |
|  * File Schema
 | |
|  * @public
 | |
|  */
 | |
| File.init(
 | |
|     {
 | |
|         id: {
 | |
|             type: DataTypes.INTEGER,
 | |
|             autoIncrement: true,
 | |
|             primaryKey: true
 | |
|         },
 | |
|         url: {
 | |
|             type: DataTypes.STRING(255),
 | |
|             allowNull: false
 | |
|         },
 | |
|         name: {
 | |
|             type: DataTypes.STRING(255),
 | |
|             defaultValue: null
 | |
|         },
 | |
|         title: {
 | |
|             type: DataTypes.STRING(255),
 | |
|             defaultValue: null
 | |
|         },
 | |
|         payload: {
 | |
|             type: DataTypes.JSONB,
 | |
|             defaultValue: null // id | code | name
 | |
|         },
 | |
| 
 | |
|         // manager
 | |
|         is_active: {
 | |
|             type: DataTypes.BOOLEAN,
 | |
|             defaultValue: true
 | |
|         },
 | |
|         created_at: {
 | |
|             type: DataTypes.DATE,
 | |
|             defaultValue: DataTypes.NOW
 | |
|         },
 | |
|         updated_at: {
 | |
|             type: DataTypes.DATE,
 | |
|             defaultValue: DataTypes.NOW
 | |
|         },
 | |
|         created_by: {
 | |
|             type: DataTypes.JSONB,
 | |
|             defaultValue: null // id | name
 | |
|         },
 | |
|         download_count: {
 | |
|             type: DataTypes.INTEGER,
 | |
|             defaultValue: 0
 | |
|         }
 | |
|     },
 | |
|     {
 | |
|         timestamps: false,
 | |
|         sequelize: sequelize,
 | |
|         schema: serviceName,
 | |
|         modelName: 'file',
 | |
|         tableName: 'tbl_files'
 | |
|     }
 | |
| );
 | |
| 
 | |
| /**
 | |
|  * Register event emiter
 | |
|  */
 | |
| File.Events = {
 | |
|     File_CREATED: `${serviceName}.file.created`,
 | |
|     File_UPDATED: `${serviceName}.file.updated`,
 | |
|     File_DELETED: `${serviceName}.file.deleted`,
 | |
| };
 | |
| File.EVENT_SOURCE = `${serviceName}.file`;
 | |
| 
 | |
| /**
 | |
|  * Add your
 | |
|  * - pre-save hooks
 | |
|  * - validations
 | |
|  * - virtuals
 | |
|  */
 | |
| File.addHook('afterCreate', () => { });
 | |
| 
 | |
| File.addHook('afterUpdate', () => { });
 | |
| 
 | |
| File.addHook('afterDestroy', () => { });
 | |
| 
 | |
| /**
 | |
|  * Load query
 | |
|  * @param {*} params
 | |
|  */
 | |
| function filterConditions(params) {
 | |
|     const options = omitBy(params, isNil);
 | |
|     options.is_active = true;
 | |
| 
 | |
|     // TODO: load condition
 | |
|     if (options.name) {
 | |
|         options.name = {
 | |
|             [Op.iLike]: `%${options.name}%`
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     return options;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Load sort query
 | |
|  * @param {*} sort_by
 | |
|  * @param {*} order_by
 | |
|  */
 | |
| function sortConditions({ sort_by, order_by }) {
 | |
|     let sort = null;
 | |
|     switch (sort_by) {
 | |
|         case 'created_at':
 | |
|             sort = ['created_at', order_by];
 | |
|             break;
 | |
|         case 'updated_at':
 | |
|             sort = ['updated_at', order_by];
 | |
|             break;
 | |
|         default: sort = ['created_at', 'DESC'];
 | |
|             break;
 | |
|     }
 | |
|     return sort;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Transform postgres model to expose object
 | |
|  */
 | |
| File.transform = (params) => {
 | |
|     const transformed = {};
 | |
|     const fields = [
 | |
|         'id',
 | |
|         'name',
 | |
|         'payload',
 | |
|         'created_by'
 | |
|     ];
 | |
|     fields.forEach((field) => {
 | |
|         transformed[field] = params[field];
 | |
|     });
 | |
| 
 | |
|     // pipe date
 | |
|     const dateFields = [
 | |
|         'created_at',
 | |
|         'updated_at'
 | |
|     ];
 | |
|     dateFields.forEach((field) => {
 | |
|         if (params[field]) {
 | |
|             transformed[field] = moment(params[field]).unix();
 | |
|         } else {
 | |
|             transformed[field] = null;
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     return transformed;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Get all changed properties
 | |
|  */
 | |
| File.getChangedProperties = ({ newModel, oldModel }) => {
 | |
|     const changedProperties = [];
 | |
|     const allChangableProperties = [
 | |
|         'id',
 | |
|         'name',
 | |
|         'payload',
 | |
|     ];
 | |
|     if (!oldModel) {
 | |
|         return allChangableProperties;
 | |
|     }
 | |
| 
 | |
|     allChangableProperties.forEach((field) => {
 | |
|         if (
 | |
|             !isUndefined(newModel[field]) &&
 | |
|             !isEqual(newModel[field], oldModel[field])
 | |
|         ) {
 | |
|             changedProperties.push(field);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     return changedProperties;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Detail
 | |
|  *
 | |
|  * @public
 | |
|  * @param {string} id
 | |
|  */
 | |
| File.get = async (id) => {
 | |
|     try {
 | |
|         const data = await File.findOne({
 | |
|             where: {
 | |
|                 id,
 | |
|                 is_active: true
 | |
|             }
 | |
|         });
 | |
|         if (!data) {
 | |
|             throw new APIError({
 | |
|                 status: httpStatus.NOT_FOUND,
 | |
|                 message: 'Không tìm thấy địa chỉ tỉnh/thành!'
 | |
|             });
 | |
|         }
 | |
|         return data;
 | |
|     } catch (ex) {
 | |
|         throw ex;
 | |
|     }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * List users in descending order of 'createdAt' timestamp.
 | |
|  *
 | |
|  * @param {number} skip - Number of users to be skipped.
 | |
|  * @param {number} limit - Limit number of users to be returned.
 | |
|  * @returns {Promise<Supplider[]>}
 | |
|  */
 | |
| File.list = async ({
 | |
|     name,
 | |
|     // sort
 | |
|     sort_by,
 | |
|     order_by,
 | |
|     skip = 0,
 | |
|     limit = 20,
 | |
| }) => {
 | |
|     const options = filterConditions({
 | |
|         name
 | |
|     });
 | |
|     const sort = sortConditions({ sort_by, order_by });
 | |
|     return File.findAll({
 | |
|         where: options,
 | |
|         order: [sort],
 | |
|         offset: skip,
 | |
|         limit: limit
 | |
|     });
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Total records.
 | |
|  *
 | |
|  * @param {number} skip - Number of users to be skipped.
 | |
|  * @param {number} limit - Limit number of users to be returned.
 | |
|  * @returns {Promise<Number>}
 | |
|  */
 | |
| File.totalRecords = ({
 | |
|     name
 | |
| }) => {
 | |
|     const options = filterConditions({
 | |
|         name
 | |
|     });
 | |
| 
 | |
|     return File.count({ where: options });
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Filter only allowed fields from File
 | |
|  *
 | |
|  * @param {Object} params
 | |
|  */
 | |
| File.filterParams = (params) => pick(params, PUBLIC_FIELDS);
 | |
| 
 | |
| /**
 | |
|  * @typedef File
 | |
|  */
 | |
| export default File;
 | |
| 
 |