Pārlūkot izejas kodu

1、每日用户登录记录;

Lawsun 1 gadu atpakaļ
vecāks
revīzija
e0d9aaa3dd

+ 42 - 32
.idea/workspace.xml

@@ -1,9 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="ChangeListManager">
-    <list default="true" id="08cfebb0-a4a4-47ee-a307-566aa06f29bb" name="Default Changelist" comment="1、...;">
+    <list default="true" id="08cfebb0-a4a4-47ee-a307-566aa06f29bb" name="Default Changelist" comment="1、时间倒序;">
+      <change afterPath="$PROJECT_DIR$/app/models/mysql/user_login_logs.js" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/app/schedule/dayUsers.js" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/app/controller/home/statistic.js" beforeDir="false" afterPath="$PROJECT_DIR$/app/controller/home/statistic.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/app/controller/home/RelInviter.js" beforeDir="false" afterPath="$PROJECT_DIR$/app/controller/home/RelInviter.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/app/controller/home/user.js" beforeDir="false" afterPath="$PROJECT_DIR$/app/controller/home/user.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/app/service/manager.js" beforeDir="false" afterPath="$PROJECT_DIR$/app/service/manager.js" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/node_modules/egg-ts-helper/.tmp/eggInfo.json" beforeDir="false" afterPath="$PROJECT_DIR$/node_modules/egg-ts-helper/.tmp/eggInfo.json" afterDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
@@ -48,34 +52,34 @@
     <option name="hideEmptyMiddlePackages" value="true" />
     <option name="showLibraryContents" value="true" />
   </component>
-  <component name="PropertiesComponent">{
-  &quot;keyToString&quot;: {
-    &quot;ASKED_MARK_IGNORED_FILES_AS_EXCLUDED&quot;: &quot;true&quot;,
-    &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
-    &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
-    &quot;SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
-    &quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
-    &quot;last_opened_file_path&quot;: &quot;D:/ysjjCode/shop-system/app/models/mysql&quot;,
-    &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
-    &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
-    &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
-    &quot;settings.editor.selected.configurable&quot;: &quot;preferences.keymap&quot;,
-    &quot;ts.external.directory.path&quot;: &quot;D:\\Program Files (x86)\\WebStorm 2022.2.1\\plugins\\JavaScriptLanguage\\jsLanguageServicesImpl\\external&quot;,
-    &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
+  <component name="PropertiesComponent"><![CDATA[{
+  "keyToString": {
+    "ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true",
+    "RunOnceActivity.OpenProjectViewOnStart": "true",
+    "RunOnceActivity.ShowReadmeOnStart": "true",
+    "SHARE_PROJECT_CONFIGURATION_FILES": "true",
+    "WebServerToolWindowFactoryState": "false",
+    "last_opened_file_path": "D:/ysjjCode/shop-system/app/models/mysql",
+    "node.js.detected.package.eslint": "true",
+    "node.js.selected.package.eslint": "(autodetect)",
+    "nodejs_package_manager_path": "npm",
+    "settings.editor.selected.configurable": "preferences.keymap",
+    "ts.external.directory.path": "D:\\Program Files (x86)\\WebStorm 2022.2.1\\plugins\\JavaScriptLanguage\\jsLanguageServicesImpl\\external",
+    "vue.rearranger.settings.migration": "true"
   },
-  &quot;keyToStringList&quot;: {
-    &quot;DatabaseDriversLRU&quot;: [
-      &quot;mysql&quot;
+  "keyToStringList": {
+    "DatabaseDriversLRU": [
+      "mysql"
     ]
   }
-}</component>
+}]]></component>
   <component name="RecentsManager">
     <key name="CopyFile.RECENT_KEYS">
       <recent name="D:\ysjjCode\shop-system\app\models\mysql" />
+      <recent name="D:\ysjjCode\shop-system\app\schedule" />
       <recent name="D:\ysjjCode\shop-system\app\controller\home" />
       <recent name="D:\ysjjCode\shop-system\app\service" />
       <recent name="D:\IdeaWorkSpace\wsProjects\shop-system\app\models\mysql" />
-      <recent name="D:\IdeaWorkSpace\wsProjects\shop-system\app\controller\home" />
     </key>
     <key name="MoveFile.RECENT_KEYS">
       <recent name="D:\IdeaWorkSpace\wsProjects\shop-system\app\service" />
@@ -259,14 +263,13 @@
       <workItem from="1691547091961" duration="8363000" />
       <workItem from="1691635063702" duration="14800000" />
       <workItem from="1692081432949" duration="2142000" />
-      <workItem from="1692151512704" duration="2134000" />
-    </task>
-    <task id="LOCAL-00024" summary="1.rearrange code;">
-      <created>1671158424385</created>
-      <option name="number" value="00024" />
-      <option name="presentableId" value="LOCAL-00024" />
-      <option name="project" value="LOCAL" />
-      <updated>1671158424385</updated>
+      <workItem from="1692151512704" duration="4784000" />
+      <workItem from="1692174536256" duration="9237000" />
+      <workItem from="1692412892850" duration="4078000" />
+      <workItem from="1692582714108" duration="4090000" />
+      <workItem from="1692666690140" duration="5486000" />
+      <workItem from="1692752289886" duration="4795000" />
+      <workItem from="1692840290649" duration="31392000" />
     </task>
     <task id="LOCAL-00025" summary="1.支持配置无分佣的分类,该分类下商品推荐购买不获得分佣;">
       <created>1671889807711</created>
@@ -604,7 +607,14 @@
       <option name="project" value="LOCAL" />
       <updated>1691747306397</updated>
     </task>
-    <option name="localTasksCounter" value="73" />
+    <task id="LOCAL-00073" summary="1、时间倒序;">
+      <created>1692153818839</created>
+      <option name="number" value="00073" />
+      <option name="presentableId" value="LOCAL-00073" />
+      <option name="project" value="LOCAL" />
+      <updated>1692153818840</updated>
+    </task>
+    <option name="localTasksCounter" value="74" />
     <servers />
   </component>
   <component name="TypeScriptGeneratedFilesManager">
@@ -650,7 +660,6 @@
     </option>
   </component>
   <component name="VcsManagerConfiguration">
-    <MESSAGE value="1.增加合作餐店列表接口;" />
     <MESSAGE value="1.补充餐店经纬度位置;2.补充餐店订单更新排名;" />
     <MESSAGE value="1.补充餐店排序字段;" />
     <MESSAGE value="1.参数错误文案调整;" />
@@ -675,7 +684,8 @@
     <MESSAGE value="1、统计;" />
     <MESSAGE value="1、用户统计详情;" />
     <MESSAGE value="1、我邀请的用户活跃列表;" />
-    <option name="LAST_COMMIT_MESSAGE" value="1、我邀请的用户活跃列表;" />
+    <MESSAGE value="1、时间倒序;" />
+    <option name="LAST_COMMIT_MESSAGE" value="1、时间倒序;" />
   </component>
   <component name="XDebuggerManager">
     <breakpoint-manager>

+ 2 - 2
app/controller/home/RelInviter.js

@@ -7,7 +7,7 @@ module.exports = class RelInviterController extends Base {
 
   /**
    * 接口测试函数
-   * http://test.enroll.sizhijie.com/oneshop/api/test
+   * http://ysjj.sizhijie.com/oneshop/api/test
    * @return {Promise<*>}
    */
   async test() {
@@ -22,7 +22,7 @@ module.exports = class RelInviterController extends Base {
     // await that.changOpenid();
 
     // 2023/3/5 分佣发放
-    that.service.commission.orderCommissionDispatchAfterPay(4);
+    // that.service.commission.orderCommissionDispatchAfterPay(17);
 
     // 2023/3/5 餐币发放
     // const dispatchResult = that.service.diningCoin.orderDiningCoinDispatchAfterPay(362);

+ 6 - 0
app/controller/home/user.js

@@ -334,6 +334,12 @@ module.exports = class UserController extends shopController {
       }
     } else {
       user.isNew = false;
+      // 2023/8/25 更新登录时间
+      const updateBean = await that.app.comoBean.instance({
+        login_time: that.app.szjcomo.date('Y-m-d H:i:s'),
+        update_ttime: that.app.szjcomo.date('Y-m-d H:i:s'),
+      }, { where: { user_id: user.user_id } });
+      await that.service.base.update(updateBean, that.useModel, '微信用户登录时间更新失败,请重试');
     }
     return user;
   }

+ 48 - 0
app/models/mysql/user_login_logs.js

@@ -0,0 +1,48 @@
+/* indent size: 2 */
+
+// eslint-disable-next-line strict
+module.exports = app => {
+  const DataTypes = app.Sequelize;
+
+  const Model = app.model.define('UserLoginLogs', {
+    id: {
+      type: DataTypes.INTEGER(10).UNSIGNED,
+      allowNull: false,
+      primaryKey: true,
+      autoIncrement: true,
+    },
+    user_id: {
+      type: DataTypes.INTEGER(10),
+      allowNull: true,
+    },
+    nickname: {
+      type: DataTypes.STRING(128),
+      allowNull: true,
+    },
+    headimgurl: {
+      type: DataTypes.STRING(256),
+      allowNull: true,
+    },
+    login_time: {
+      type: DataTypes.TIME,
+      allowNull: true,
+    },
+    update_ttime: {
+      type: DataTypes.TIME,
+      allowNull: true,
+    },
+    create_time: {
+      type: DataTypes.TIME,
+      allowNull: true,
+    },
+  }, {
+    tableName: 'szj_users_login_logs',
+  });
+
+  Model.associate = function() {
+
+  };
+  // 同步:没有就新建,有就不变
+  Model.sync();
+  return Model;
+};

+ 61 - 0
app/schedule/dayUsers.js

@@ -0,0 +1,61 @@
+'use strict';
+const Subscription = require('egg').Subscription;
+
+// 2023/8/25 每日统计登录人数
+class dayUsers extends Subscription {
+  /**
+   * cron
+   * *    *    *    *    *    *
+   * ┬    ┬    ┬    ┬    ┬    ┬
+   * │    │    │    │    │    |
+   * │    │    │    │    │    └ day of week (0 - 7) (0 or 7 is Sun)
+   * │    │    │    │    └───── month (1 - 12)
+   * │    │    │    └────────── day of month (1 - 31)
+   * │    │    └─────────────── hour (0 - 23)
+   * │    └──────────────────── minute (0 - 59)
+   * └───────────────────────── second (0 - 59, optional)
+   * @returns {{cron: string, type: string}}
+   */
+  // 通过 schedule 属性来设置定时任务的执行间隔等配置
+  static get schedule() {
+    return {
+      // interval: '1m', // 1 分钟间隔
+      // interval: '10s', // 10s
+      // cron: '0 0 */3 * * *', // 每三小时准点执行一次
+      cron: '0 58 23 * * *', // 每天23:58执行一次
+      type: 'all', // 指定所有的 worker 都需要执行
+    };
+  }
+
+  // subscribe 是真正定时任务执行时被运行的函数
+  async subscribe() {
+    const that = this;
+    const seq = that.app.Sequelize;
+    const tmpTime = that.app.szjcomo.time();
+    const transaction = await that.app.model.transaction();
+    try {
+      const users = await that.app.model.Users.findAll({
+        where: { update_ttime: { [seq.Op.between]: [ `${that.app.szjcomo.date('Y-m-d', tmpTime)} 00:00:00`, `${that.app.szjcomo.date('Y-m-d', tmpTime)} 23:59:59` ] } },
+        order: [[ 'update_ttime', 'asc' ]],
+        attributes: [ 'user_id', 'nickname', 'headimgurl', 'login_time', 'update_ttime' ],
+      });
+
+      // 2023/8/27 批量创建用户登录记录
+      const datas = await users.map(item => {
+        return Object.assign(item, {
+          create_time: that.app.szjcomo.date('Y-m-d H:i:s'),
+        });
+      });
+      await that.app.model.UserLoginLogs.bulkCreate(datas, { transaction });
+      transaction.commit();
+    } catch (e) {
+      console.log(e);
+      if (transaction) {
+        transaction.rollback();
+      }
+    }
+  }
+
+}
+
+module.exports = dayUsers;

+ 139 - 135
app/service/manager.js

@@ -1,144 +1,148 @@
 'use strict';
-//基类服务
+// 基类服务
 const BaseService = require('./base.js');
 /**
  * [exports 后台管理经理基类服务]
  * @type {[type]}
  */
 module.exports = class ManagerService extends BaseService {
-	/*==================================session相关=====================================*/
-	/**
-	 * [ActionAdminUserId 当前后台操作的管理员ID]
-	 * @author    szjcomo
-	 * @date   		2020-11-02
-	 */
-	ActionAdminUserId() {
-		let that = this;
-		let token = that.ctx.request.header.authorization;
-		let user = that.app.jwt.verify(token,that.app.config.jwt.secret);
-		return user.admin_id;
-	}
-	/**
-	 * [ActionAdminUser 获取当前管理员]
-	 * @author    szjcomo
-	 * @date   		2021-02-27
-	 */
-	ActionAdminUser() {
-		let that = this;
-		let token = that.ctx.request.header.authorization;
-		let user = that.app.jwt.verify(token,that.app.config.jwt.secret);
-		return user;
-	}
-	/**
-	 * [getRoleAuth 获取角色权限]
-	 * @author    szjcomo
-	 * @date   		2020-11-02
-	 * @param  {[type]}     role_id    	[description]
-	 * @param  {Boolean}    is_checked 	[description]
-	 * @param  {Number}     mobile  	[是否手机端访问]
-	 * @return {[type]}                	[description]
-	 */
-	async getRoleAuth(role_id,is_checked = false,mobile = 0) {
-		let that = this;
-		let seq = that.app.Sequelize;
-		let idxs = [];
-		let result = [];
-		let options = {
-			attributes:['access_id','access_name','access_icon','pid','is_nav','router_name','router_path','vuecomponent','mobile_show'],
-			raw:true,where:{access_status:1},order:[['access_sort','asc'],['access_id','asc']]
-		};
-		if(mobile) options.where.mobile_show = 1;
-		let access = await that.app.model.Accesss.findAll(options);
-		if(role_id > 0) {
-			let tmpOptions = {where:{role_id:role_id},attributes:['access_id'],raw:true};	
-			let tmpAccess = await that.app.model.AccessRole.findOne(tmpOptions);
-			if(tmpAccess) idxs = (tmpAccess.access_id.split(','));
-		} else {
-			result = access;
-		}
-		if(result.length == 0) {
-			access.forEach(item => {
-				if(that.app.szjcomo.inArray(idxs,`${item.access_id}`)) {
-					item.checked = true;
-				} else {
-					item.checked = false;
-				}
-				if(is_checked == false) {
-					result.push(item);
-				} else {
-					if(item.checked) result.push(item);
-				}
-			})			
-		}
-		let finalResult = that.app.szjcomo.arrayRecursion(result,0,'pid','access_id','child');
-		return finalResult;
-	}
-	/**
-	 * [getLevel 获取分类节点等级]
-	 * @author    szjcomo
-	 * @date   		2020-10-26
-	 * @param  {Number}     pid         [description]
-	 * @param  {[type]}     actionModel [description]
-	 * @param  {Object}     options     [description]
-	 * @return {[type]}                 [description]
-	 */
-	async getLevel(pid = 0,actionModel = null,options = {}) {
-		if(pid == 0) return 1;
-		let result = await actionModel.findOne(options);
-		return (result.level + 1);
-	}
+  /* ==================================session相关=====================================*/
+  /**
+   * [ActionAdminUserId 当前后台操作的管理员ID]
+   * @author    szjcomo
+   * @date        2020-11-02
+   */
+  ActionAdminUserId() {
+    const that = this;
+    const token = that.ctx.request.header.authorization;
+    const user = that.app.jwt.verify(token, that.app.config.jwt.secret);
+    return user.admin_id;
+  }
 
-	/*==================================临时方法=====================================*/
-	/**
-	 * [console 控制台数据]
-	 * @author    szjcomo
-	 * @date   		2020-11-02
-	 * @return {[type]}     [description]
-	 */
-	async console() {
-		let that = this;
-		let cacheKey = `${process.env.APP_CUSTOME}_console_data`;
-		let result = await that.service.redis.get(cacheKey);
-		if(!result) {
-			result = {
-				/**
-				 * [quick_data 快捷处理]
-				 * @type {Array}
-				 */
-				quick_data:[
-			        {icon:'&#xe624;',title:'控制台',router_name:'system_dashboard'},
-			        {icon:'&#xe621;',title:'管理员列表',router_name:'admin_user'},
-			        {icon:'&#xe68b;',title:'订单列表',router_name:'select_orders'},
-			        {icon:'&#xe64a;',title:'商品列表',router_name:'select_products'},
-			        {icon:'&#xe770;',title:'配置设置',router_name:'config_setting'},
-			        {icon:'&#xe642;',title:'用户列表',router_name:'select_users'},
-			        {icon:'&#xe601;',title:'支付配置',router_name:'select_pays_config'},
-			        {icon:'&#xe644;',title:'商品分类',router_name:'select_product_category'}
-				],
-				baseData:await that.service.order.orderDataCount(),
-				saleData:await that.service.order.saleDataCount()
-			};
-			await that.service.redis.set(cacheKey,result,10*60);
-		}
-		return result;
-	}
-	/**
-	 * [adminConfig 后台管理配置]
-	 * @author    szjcomo
-	 * @date   		2021-08-08
-	 * @return {[type]}     [description]
-	 */
-	async adminConfig(){
-		let that = this;
-		let configs = await that.app.model.Configs.findAll({
-			where:{field_index:{[that.app.Sequelize.Op.in]:['website_title','files_upload_uri','image_ocr_uri','editor_templates_uri']}},
-			attributes:['field_value','field_index'],raw:true
-		});
-		let result = {};
-		configs.forEach(item => {
-			result[item.field_index] = item.field_value;
-		})
-		return result;
-	}
+  /**
+   * [ActionAdminUser 获取当前管理员]
+   * @author    szjcomo
+   * @date        2021-02-27
+   */
+  ActionAdminUser() {
+    const that = this;
+    const token = that.ctx.request.header.authorization;
+    const user = that.app.jwt.verify(token, that.app.config.jwt.secret);
+    return user;
+  }
 
-}
+  /**
+   * [getRoleAuth 获取角色权限]
+   * @author    szjcomo
+   * @date        2020-11-02
+   * @param  {[type]}     role_id        [description]
+   * @param  {Boolean}    is_checked    [description]
+   * @param  {Number}     mobile    [是否手机端访问]
+   * @return {[type]}                    [description]
+   */
+  async getRoleAuth(role_id, is_checked = false, mobile = 0) {
+    const that = this;
+    // const seq = that.app.Sequelize;
+    let idxs = [];
+    let result = [];
+    const options = {
+      attributes: [ 'access_id', 'access_name', 'access_icon', 'pid', 'is_nav', 'router_name', 'router_path', 'vuecomponent', 'mobile_show' ],
+      raw: true, where: { access_status: 1 }, order: [[ 'access_sort', 'asc' ], [ 'access_id', 'asc' ]],
+    };
+    if (mobile) options.where.mobile_show = 1;
+    const access = await that.app.model.Accesss.findAll(options);
+    if (role_id > 0) {
+      const tmpOptions = { where: { role_id }, attributes: [ 'access_id' ], raw: true };
+      const tmpAccess = await that.app.model.AccessRole.findOne(tmpOptions);
+      if (tmpAccess) idxs = (tmpAccess.access_id.split(','));
+    } else {
+      result = access;
+    }
+    if (result.length == 0) {
+      access.forEach(item => {
+        if (that.app.szjcomo.inArray(idxs, `${item.access_id}`)) {
+          item.checked = true;
+        } else {
+          item.checked = false;
+        }
+        if (is_checked == false) {
+          result.push(item);
+        } else {
+          if (item.checked) result.push(item);
+        }
+      });
+    }
+    const finalResult = that.app.szjcomo.arrayRecursion(result, 0, 'pid', 'access_id', 'child');
+    return finalResult;
+  }
+
+  /**
+   * [getLevel 获取分类节点等级]
+   * @author    szjcomo
+   * @date        2020-10-26
+   * @param  {Number}     pid         [description]
+   * @param  {[type]}     actionModel [description]
+   * @param  {Object}     options     [description]
+   * @return {[type]}                 [description]
+   */
+  async getLevel(pid = 0, actionModel = null, options = {}) {
+    if (pid == 0) return 1;
+    const result = await actionModel.findOne(options);
+    return (result.level + 1);
+  }
+
+  /* ==================================临时方法=====================================*/
+  /**
+   * [console 控制台数据]
+   * @author    szjcomo
+   * @date        2020-11-02
+   * @return {[type]}     [description]
+   */
+  async console() {
+    const that = this;
+    const cacheKey = `${process.env.APP_CUSTOME}_console_data`;
+    let result = await that.service.redis.get(cacheKey);
+    if (!result) {
+      result = {
+        /**
+         * [quick_data 快捷处理]
+         * @type {Array}
+         */
+        quick_data: [
+          { icon: '&#xe624;', title: '控制台', router_name: 'system_dashboard' },
+          { icon: '&#xe621;', title: '管理员列表', router_name: 'admin_user' },
+          { icon: '&#xe68b;', title: '订单列表', router_name: 'select_orders' },
+          { icon: '&#xe64a;', title: '商品列表', router_name: 'select_products' },
+          { icon: '&#xe770;', title: '配置设置', router_name: 'config_setting' },
+          { icon: '&#xe642;', title: '用户列表', router_name: 'select_users' },
+          { icon: '&#xe601;', title: '支付配置', router_name: 'select_pays_config' },
+          { icon: '&#xe644;', title: '商品分类', router_name: 'select_product_category' },
+        ],
+        baseData: await that.service.order.orderDataCount(),
+        saleData: await that.service.order.saleDataCount(),
+      };
+      await that.service.redis.set(cacheKey, result, 10 * 60);
+    }
+    return result;
+  }
+
+  /**
+   * [adminConfig 后台管理配置]
+   * @author    szjcomo
+   * @date        2021-08-08
+   * @return {[type]}     [description]
+   */
+  async adminConfig() {
+    const that = this;
+    const configs = await that.app.model.Configs.findAll({
+      where: { field_index: { [that.app.Sequelize.Op.in]: [ 'website_title', 'files_upload_uri', 'image_ocr_uri', 'editor_templates_uri' ] } },
+      attributes: [ 'field_value', 'field_index' ], raw: true,
+    });
+    const result = {};
+    configs.forEach(item => {
+      result[item.field_index] = item.field_value;
+    });
+    return result;
+  }
+
+};

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
node_modules/egg-ts-helper/.tmp/eggInfo.json


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels