Переглянути джерело

Merge remote-tracking branch 'origin/master'

xuchao 4 місяців тому
батько
коміт
06f572e772
100 змінених файлів з 4903 додано та 179 видалено
  1. 2 1
      snowy-admin-web/index.html
  2. BIN
      snowy-admin-web/public/img/login_background2.png
  3. BIN
      snowy-admin-web/public/img/tanglogo1.png
  4. 28 0
      snowy-admin-web/src/api/biz/bizActivateApi.js
  5. 28 0
      snowy-admin-web/src/api/biz/bizDisableRecordApi.js
  6. 15 1
      snowy-admin-web/src/api/biz/bizUserApi.js
  7. 24 0
      snowy-admin-web/src/api/biz/consumptionRecordApi.js
  8. 2 1
      snowy-admin-web/src/components/HomeCard/BizSlideshowCard/index.vue
  9. 2 2
      snowy-admin-web/src/components/XnRoleSelector/index.vue
  10. 2 1
      snowy-admin-web/src/config/index.js
  11. 1 0
      snowy-admin-web/src/layout/index.vue
  12. 2 2
      snowy-admin-web/src/views/auth/login/login.less
  13. 1 1
      snowy-admin-web/src/views/auth/login/login.vue
  14. 82 0
      snowy-admin-web/src/views/biz/bizactivate/form.vue
  15. 151 0
      snowy-admin-web/src/views/biz/bizactivate/index.vue
  16. 13 4
      snowy-admin-web/src/views/biz/consumptionrecord/index.vue
  17. 10 11
      snowy-admin-web/src/views/biz/couponrecord/index.vue
  18. 96 0
      snowy-admin-web/src/views/biz/member/addUser.vue
  19. 7 7
      snowy-admin-web/src/views/biz/member/consumption.vue
  20. 104 0
      snowy-admin-web/src/views/biz/member/disable.vue
  21. 53 12
      snowy-admin-web/src/views/biz/member/form.vue
  22. 140 0
      snowy-admin-web/src/views/biz/member/impExp.vue
  23. 66 11
      snowy-admin-web/src/views/biz/member/index.vue
  24. 3 3
      snowy-admin-web/src/views/biz/org/index.vue
  25. 17 9
      snowy-admin-web/src/views/biz/rebaterecord/index.vue
  26. 75 10
      snowy-admin-web/src/views/biz/rechargeplanconfig/form.vue
  27. 68 11
      snowy-admin-web/src/views/biz/rechargeplanconfig/index.vue
  28. 5 5
      snowy-admin-web/src/views/biz/rechargerecord/form.vue
  29. 21 13
      snowy-admin-web/src/views/biz/rechargerecord/index.vue
  30. 18 4
      snowy-admin-web/src/views/biz/recommendrecord/index.vue
  31. 822 0
      snowy-admin-web/src/views/biz/statisty/total.vue
  32. 122 0
      snowy-admin-web/src/views/biz/warn/detail.vue
  33. 232 0
      snowy-admin-web/src/views/biz/warn/index.vue
  34. 2 1
      snowy-admin-web/src/views/dev/slideshow/index.vue
  35. 3 3
      snowy-admin-web/src/views/sys/org/index.vue
  36. 1 1
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonCouponGeneratorUtil.java
  37. 13 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/pojo/SaBaseLoginUser.java
  38. 5 1
      snowy-plugin/snowy-plugin-biz/pom.xml
  39. 123 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/controller/BizActivateController.java
  40. 64 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/entity/BizActivate.java
  41. 34 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/enums/BizActivateEnum.java
  42. 29 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/mapper/BizActivateMapper.java
  43. 18 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/mapper/mapping/BizActivateMapper.xml
  44. 50 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/param/BizActivateAddParam.java
  45. 55 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/param/BizActivateEditParam.java
  46. 35 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/param/BizActivateIdParam.java
  47. 55 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/param/BizActivatePageParam.java
  48. 80 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/service/BizActivateService.java
  49. 94 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/service/impl/BizActivateServiceImpl.java
  50. 81 1
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/controller/ConsumptionRecordController.java
  51. 13 2
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/entity/ConsumptionRecord.java
  52. 16 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/mapper/ConsumptionRecordMapper.java
  53. 158 2
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/mapper/mapping/ConsumptionRecordMapper.xml
  54. 20 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/ConsumptionChart.java
  55. 4 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/ConsumptionRecordAddParam.java
  56. 41 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/ConsumptionRecordExportResult.java
  57. 8 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/ConsumptionRecordPageParam.java
  58. 17 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/UserBalanceResult.java
  59. 20 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/service/ConsumptionRecordService.java
  60. 261 15
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/service/impl/ConsumptionRecordServiceImpl.java
  61. 4 1
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/entity/BizCouponRecord.java
  62. 6 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/mapper/BizCouponRecordMapper.java
  63. 32 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/mapper/mapping/BizCouponRecordMapper.xml
  64. 7 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/param/BizCouponRecordPageParam.java
  65. 4 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/service/BizCouponRecordService.java
  66. 35 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/service/impl/BizCouponRecordServiceImpl.java
  67. 123 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/controller/BizDisableRecordController.java
  68. 64 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/entity/BizDisableRecord.java
  69. 34 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/enums/BizDisableRecordEnum.java
  70. 30 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/mapper/BizDisableRecordMapper.java
  71. 17 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/mapper/mapping/BizDisableRecordMapper.xml
  72. 50 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/param/BizDisableRecordAddParam.java
  73. 55 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/param/BizDisableRecordEditParam.java
  74. 35 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/param/BizDisableRecordIdParam.java
  75. 53 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/param/BizDisableRecordPageParam.java
  76. 80 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/service/BizDisableRecordService.java
  77. 99 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/service/impl/BizDisableRecordServiceImpl.java
  78. 11 4
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/controller/BizRechargePlanConfigController.java
  79. 12 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/entity/BizRechargePlanConfig.java
  80. 5 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/mapper/BizRechargePlanConfigMapper.java
  81. 19 1
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/mapper/mapping/BizRechargePlanConfigMapper.xml
  82. 9 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/param/BizRechargePlanConfigAddParam.java
  83. 9 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/param/BizRechargePlanConfigEditParam.java
  84. 2 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/param/BizRechargePlanConfigPageParam.java
  85. 14 3
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/service/impl/BizRechargePlanConfigServiceImpl.java
  86. 8 8
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargerecord/mapper/mapping/BizRechargeRecordMapper.xml
  87. 2 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargerecord/param/BizRechargeRecordPageParam.java
  88. 22 4
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargerecord/service/impl/BizRechargeRecordServiceImpl.java
  89. 8 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/recommendrecord/entity/BizRecommendRecord.java
  90. 4 2
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/recommendrecord/mapper/mapping/BizRecommendRecordMapper.xml
  91. 11 1
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/recommendrecord/service/impl/BizRecommendRecordServiceImpl.java
  92. 45 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/task/couponRecordTask.java
  93. 46 4
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/controller/BizUserController.java
  94. 13 1
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/param/BizUserAddParam.java
  95. 20 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/param/BizUserImport.java
  96. 3 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/param/BizUserPageParam.java
  97. 6 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/result/BizMemberUserResult.java
  98. 11 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/service/BizUserService.java
  99. 272 15
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/service/impl/BizUserServiceImpl.java
  100. 111 0
      snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/utils/CommonExportUtil.java

+ 2 - 1
snowy-admin-web/index.html

@@ -4,7 +4,8 @@
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
-  <link rel="icon" href="/favicon.ico">
+<!--  <link rel="icon" href="/favicon.ico">-->
+	<link rel="icon" href="/img/tanglogo1.png">
   <title>Snowy</title>
   <style>
     .dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1677FF;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}

BIN
snowy-admin-web/public/img/login_background2.png


BIN
snowy-admin-web/public/img/tanglogo1.png


+ 28 - 0
snowy-admin-web/src/api/biz/bizActivateApi.js

@@ -0,0 +1,28 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/biz/bizactivate/` + url, ...arg)
+
+/**
+ * 用户充值待激活记录Api接口管理器
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+export default {
+	// 获取用户充值待激活记录分页
+	bizActivatePage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交用户充值待激活记录表单 edit为true时为编辑,默认为新增
+	bizActivateSubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除用户充值待激活记录
+	bizActivateDelete(data) {
+		return request('delete', data)
+	},
+	// 获取用户充值待激活记录详情
+	bizActivateDetail(data) {
+		return request('detail', data, 'get')
+	}
+}

+ 28 - 0
snowy-admin-web/src/api/biz/bizDisableRecordApi.js

@@ -0,0 +1,28 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/biz/disablerecord/` + url, ...arg)
+
+/**
+ * 清除记录Api接口管理器
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+export default {
+	// 获取清除记录分页
+	bizDisableRecordPage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交清除记录表单 edit为true时为编辑,默认为新增
+	bizDisableRecordSubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除清除记录
+	bizDisableRecordDelete(data) {
+		return request('delete', data)
+	},
+	// 获取清除记录详情
+	bizDisableRecordDetail(data) {
+		return request('detail', data, 'get')
+	}
+}

+ 15 - 1
snowy-admin-web/src/api/biz/bizUserApi.js

@@ -42,6 +42,10 @@ export default {
 	userDisableUser(data) {
 		return request('disableUser', data)
 	},
+	// 禁用人员
+	userDisableCustomer(data) {
+		return request('disableCustomer', data)
+	},
 	// 启用人员
 	userEnableUser(data) {
 		return request('enableUser', data)
@@ -85,5 +89,15 @@ export default {
 		return request('exportUserInfo', data, 'get', {
 			responseType: 'blob'
 		})
-	}
+	},
+	// 下载导入模板
+	downloadImportTemplate(data) {
+		return request('down', data, 'get', {
+			responseType: 'blob'
+		})
+	},
+	// 导入
+	itemImport(data) {
+		return request('import', data)
+	},
 }

+ 24 - 0
snowy-admin-web/src/api/biz/consumptionRecordApi.js

@@ -24,5 +24,29 @@ export default {
 	// 获取消费记录详情
 	consumptionRecordDetail(data) {
 		return request('detail', data, 'get')
+	},
+	// 获取消费金额统计
+	queryRecordTotal(data){
+		return request('queryRecordTotal',data,'get')
+	},
+	//获取账户余额&会员数统计
+	queryBalanceTotal(data){
+		return request('queryBalanceTotal',data,'get')
+	},
+	//近七日消费金额和订单数折线图
+	queryConsumptionChart(data){
+		return request('queryConsumptionChart',data,'get')
+	},
+	queryEachStore(data){
+		return request('queryEachStore',data,'get')
+	},
+	//导出
+	exportRecord(data){
+		return request('exportRecord', data, 'get', {
+			responseType: 'blob'
+		})
+	},
+	warnPage(data){
+		return request('warnPage',data,'get')
 	}
 }

+ 2 - 1
snowy-admin-web/src/components/HomeCard/BizSlideshowCard/index.vue

@@ -12,7 +12,7 @@
 			<img
 				v-for="item in slideshowList"
 				:key="item.id"
-				:src="item.image"
+				:src="sysConfig.API_URL+item.image"
 				class="carousel-images"
 				@click="leaveForOpen(item.pathDetails)"
 			/>
@@ -23,6 +23,7 @@
 
 <script setup name="carousel">
 	import bizIndexApi from '@/api/biz/bizIndexApi'
+	import sysConfig from "@/config";
 	import { Empty } from 'ant-design-vue'
 	import { isEmpty, cloneDeep } from 'lodash-es'
 	import router from '@/router'

+ 2 - 2
snowy-admin-web/src/components/XnRoleSelector/index.vue

@@ -1,9 +1,9 @@
 <template>
 	<!-- 这是引入后展示的样式 -->
 	<div v-if="props.show">
-		<a-tag v-for="data in dataArray" closable @close="deleteObj(data)" :key="data.id" class="mb-1">{{
+<!--		<a-tag v-for="data in dataArray" closable @close="deleteObj(data)" :key="data.id" class="mb-1">{{
 			data.name
-		}}</a-tag>
+		}}</a-tag>-->
 		<a-button
 			type="primary"
 			size="small"

+ 2 - 1
snowy-admin-web/src/config/index.js

@@ -70,7 +70,8 @@ const DEFAULT_CONFIG = {
 	LANG: 'zh-cn',
 
 	// 主题颜色
-	COLOR: '#1677FF',
+	//COLOR: '#1677FF',
+	COLOR:'#FA541C',
 
 	// 默认整体主题
 	SNOWY_THEME: 'dark',

+ 1 - 0
snowy-admin-web/src/layout/index.vue

@@ -276,6 +276,7 @@
 				let localVersion = getLocalHash()
 				// 线上
 				let onlineVersion = await checkHash()
+				console.log('localVersion:',localVersion,",onlineVersion:",onlineVersion)
 				// 如果不一样,提示更新
 				if (localVersion !== onlineVersion) {
 					if (document.querySelector('.notification-update-version')) {

+ 2 - 2
snowy-admin-web/src/views/auth/login/login.less

@@ -19,8 +19,8 @@
 	height: 100%;
 	overflow: hidden;
 	background-size: cover;
-	background-position: center;
-	background-image: url(/img/login_background.png);
+	background-position: left center;
+	background-image: url(/img/login_background2.png);
 	position: relative;
 }
 @keyframes myfirst {

+ 1 - 1
snowy-admin-web/src/views/auth/login/login.vue

@@ -8,7 +8,7 @@
 					target="_blank"
 					@click="handleLink"
 				>
-					<img :alt="sysBaseConfig.SNOWY_SYS_NAME" src="/img/logo.png" />
+<!--					<img :alt="sysBaseConfig.SNOWY_SYS_NAME" src="/img/logo.png" />-->
 				</a>
 			</div>
 		</div>

+ 82 - 0
snowy-admin-web/src/views/biz/bizactivate/form.vue

@@ -0,0 +1,82 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑用户充值待激活记录' : '增加用户充值待激活记录'"
+		:width="700"
+		v-model:open="open"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
+			<a-form-item label="用户id:" name="userId">
+				<a-input v-model:value="formData.userId" placeholder="请输入用户id" allow-clear />
+			</a-form-item>
+			<a-form-item label="账户糕点:" name="accountBalance">
+				<a-input v-model:value="formData.accountBalance" placeholder="请输入账户糕点" allow-clear />
+			</a-form-item>
+			<a-form-item label="状态1:待激活    2:已激活:" name="status">
+				<a-input v-model:value="formData.status" placeholder="请输入状态1:待激活    2:已激活" allow-clear />
+			</a-form-item>
+			<a-form-item label="帮助激活人数:" name="helpCount">
+				<a-input v-model:value="formData.helpCount" placeholder="请输入帮助激活人数" allow-clear />
+			</a-form-item>
+		</a-form>
+		<template #footer>
+			<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+			<a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup name="bizActivateForm">
+	import { cloneDeep } from 'lodash-es'
+	import { required } from '@/utils/formRules'
+	import bizActivateApi from '@/api/biz/bizActivateApi'
+	// 抽屉状态
+	const open = ref(false)
+	const emit = defineEmits({ successful: null })
+	const formRef = ref()
+	// 表单数据
+	const formData = ref({})
+	const submitLoading = ref(false)
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		open.value = true
+		if (record) {
+			let recordData = cloneDeep(record)
+			formData.value = Object.assign({}, recordData)
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		formData.value = {}
+		open.value = false
+	}
+	// 默认要校验的
+	const formRules = {
+	}
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				submitLoading.value = true
+				const formDataParam = cloneDeep(formData.value)
+				bizActivateApi
+					.bizActivateSubmitForm(formDataParam, formDataParam.id)
+					.then(() => {
+						onClose()
+						emit('successful')
+					})
+					.finally(() => {
+						submitLoading.value = false
+					})
+			})
+			.catch(() => {})
+	}
+	// 抛出函数
+	defineExpose({
+		onOpen
+	})
+</script>

+ 151 - 0
snowy-admin-web/src/views/biz/bizactivate/index.vue

@@ -0,0 +1,151 @@
+<template>
+	<a-card :bordered="false" style="margin-bottom: 10px" class="mb-2">
+		<a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
+			<a-row :gutter="24">
+				<a-col :span="6">
+					<a-form-item label="姓名" name="userName">
+						<a-input v-model:value="searchFormState.userName" placeholder="姓名查询" allow-clear />
+					</a-form-item>
+				</a-col>
+				<a-col :span="6">
+					<a-form-item label="状态" name="status">
+						<a-select v-model:value="searchFormState.status" placeholder="状态查询" :options="statusList">
+						</a-select>
+					</a-form-item>
+				</a-col>
+
+				<a-col :span="6">
+					<a-button type="primary" html-type="submit" @click="tableRef.refresh()">查询</a-button>
+					<a-button style="margin: 0 8px" @click="reset">重置</a-button>
+				</a-col>
+			</a-row>
+		</a-form>
+	</a-card>
+	<a-card :bordered="false">
+		<s-table
+			ref="tableRef"
+			:columns="columns"
+			:data="loadData"
+			bordered
+			:row-key="(record) => record.id"
+
+		>
+			<template #operator class="table-operator">
+				<a-space>
+					<a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('bizActivateAdd')">
+						<template #icon><plus-outlined /></template>
+						新增
+					</a-button>
+				</a-space>
+			</template>
+			<template #bodyCell="{ column, record }">
+				<template v-if="column.dataIndex === 'action'">
+					<a-space>
+						<a @click="formRef.onOpen(record)" v-if="hasPerm('bizActivateEdit')">编辑</a>
+						<a-divider type="vertical" v-if="hasPerm(['bizActivateEdit', 'bizActivateDelete'], 'and')" />
+						<a-popconfirm title="确定要删除吗?" @confirm="deleteBizActivate(record)">
+							<a-button type="link" danger size="small" v-if="hasPerm('bizActivateDelete')">删除</a-button>
+						</a-popconfirm>
+					</a-space>
+				</template>
+				<template v-if="column.dataIndex === 'status'">
+					<a-tag
+						:color="
+							record.status === '1'
+								? '#f50'
+								: record.status === '2'
+								  ? '#87d068'
+								      : '#f50'
+						"
+					>
+						{{ $TOOL.dictTypeData('activate_status', record.status) }}
+					</a-tag>
+				</template>
+			</template>
+		</s-table>
+	</a-card>
+	<Form ref="formRef" @successful="tableRef.refresh()" />
+</template>
+
+<script setup name="bizactivate">
+	import { cloneDeep } from 'lodash-es'
+	import Form from './form.vue'
+	import bizActivateApi from '@/api/biz/bizActivateApi'
+	import tool from '@/utils/tool'
+	const tableRef = ref()
+	const formRef = ref()
+	const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+	const searchFormRef = ref()
+	let searchFormState = reactive({})
+	const statusList = tool.dictList('activate_status')
+	const columns = [
+		{
+			title: '姓名',
+			dataIndex: 'userName',
+			align: 'center',
+		},
+		{
+			title: '糕点',
+			dataIndex: 'accountBalance',
+			align: 'center',
+		},
+		{
+			title: '状态',
+			dataIndex: 'status',
+			align: 'center',
+		},
+		{
+			title: '激活需要人数',
+			dataIndex: 'successCount',
+			align: 'center',
+		},
+		{
+			title: '帮助激活人数',
+			dataIndex: 'helpCount',
+			align: 'center',
+		},
+		{
+			title: '充值时间',
+			dataIndex: 'createTime',
+			align: 'center',
+		},
+	]
+	// 操作栏通过权限判断是否显示
+	/*if (hasPerm(['bizActivateEdit', 'bizActivateDelete'])) {
+		columns.push({
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+			width: 150
+		})
+	}*/
+
+	const loadData = (parameter) => {
+		const searchFormParam = JSON.parse(JSON.stringify(searchFormState))
+		return bizActivateApi.bizActivatePage(Object.assign(parameter, searchFormParam)).then((data) => {
+			return data
+		})
+	}
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		tableRef.value.refresh(true)
+	}
+	// 删除
+	const deleteBizActivate = (record) => {
+		let params = [
+			{
+				id: record.id
+			}
+		]
+		bizActivateApi.bizActivateDelete(params).then(() => {
+			tableRef.value.refresh(true)
+		})
+	}
+	// 批量删除
+	const deleteBatchBizActivate = (params) => {
+		bizActivateApi.bizActivateDelete(params).then(() => {
+			tableRef.value.clearRefreshSelected()
+		})
+	}
+</script>

+ 13 - 4
snowy-admin-web/src/views/biz/consumptionrecord/index.vue

@@ -68,7 +68,7 @@
 					</a-button>
 				</a-space>
 			</template>
-			<template #bodyCell="{ column, record }">
+			<template #bodyCell="{ column, record,index }">
 				<template v-if="column.dataIndex === 'action'">
 					<a-space>
 						<a @click="formRef.onOpen(record)" v-if="hasPerm('consumptionRecordEdit')">编辑</a>
@@ -93,6 +93,9 @@
 						{{ $TOOL.dictTypeData('consumption_operate', record.consumptionOperate) }}
 					</a-tag>
 				</template>
+				<template v-if="column.dataIndex === 'serial'">
+					{{ index + 1 }}
+				</template>
 			</template>
 		</s-table>
 	</a-card>
@@ -122,6 +125,12 @@
 
 	const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
 	const columns = [
+		{
+			title: '序号',
+			dataIndex: 'serial',
+			align: 'center',
+			width: 50
+		},
 		{
 			title: '会员姓名',
 			dataIndex: 'userName',
@@ -138,17 +147,17 @@
 			align: 'center',
 		},
 		{
-			title: '原账户余额',
+			title: '原账户糕点',
 			dataIndex: 'accountBalance',
 			align: 'center',
 		},
 		{
-			title: '原代金券余额',
+			title: '原账户积分',
 			dataIndex: 'voucherBalance',
 			align: 'center',
 		},
 		{
-			title: '消费金额',
+			title: '消费糕点或积分',
 			dataIndex: 'consumptionMoney',
 			align: 'center',
 		},

+ 10 - 11
snowy-admin-web/src/views/biz/couponrecord/index.vue

@@ -3,8 +3,8 @@
 		<a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
 			<a-row :gutter="24">
 				<a-col :span="6">
-					<a-form-item label="优惠券编码" name="couponNo">
-						<a-input v-model:value="searchFormState.couponNo" placeholder="优惠券编码查询" />
+					<a-form-item label="蛋糕券编码" name="couponNo">
+						<a-input v-model:value="searchFormState.couponNo" placeholder="蛋糕券编码查询" />
 					</a-form-item>
 				</a-col>
 				<a-col :span="6">
@@ -33,13 +33,6 @@
 						<template #icon><plus-outlined /></template>
 						新增
 					</a-button>
-					<xn-batch-button
-						v-if="hasPerm('bizCouponRecordBatchDelete')"
-						buttonName="批量删除"
-                        icon="DeleteOutlined"
-						:selectedRowKeys="selectedRowKeys"
-						@batchCallBack="deleteBatchBizCouponRecord"
-					/>
 				</a-space>
 			</template>
 			<template #bodyCell="{ column, record , index }">
@@ -93,13 +86,19 @@
 			width: 50
 		},
 		{
-			title: '优惠券编码',
+			title: '会员姓名',
+			dataIndex: 'userName',
+			align: 'center',
+			width: 120
+		},
+		{
+			title: '蛋糕券编码',
 			dataIndex: 'couponNo',
 			align: 'center',
 			width: 200
 		},
 		{
-			title: '优惠券生成时间',
+			title: '蛋糕券生成时间',
 			dataIndex: 'time',
 			align: 'center',
 			width: 150

+ 96 - 0
snowy-admin-web/src/views/biz/member/addUser.vue

@@ -0,0 +1,96 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '增加会员' : '增加会员'"
+		:width="700"
+		v-model:open="open"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form ref="formRef" :model="formData" :rules="formRules" :label-col="labelCol" :wrapper-col="wrapperCol">
+			<a-form-item label="姓名:" name="name">
+				<a-input v-model:value="formData.name" placeholder="请输入姓名" allow-clear />
+			</a-form-item>
+			<a-form-item label="手机号:" name="phone">
+				<a-input v-model:value="formData.phone" placeholder="请输入手机号" allow-clear @blur="onBlur"/>
+			</a-form-item>
+			<a-form-item label="糕点:" name="accountBalance">
+				<a-input-number v-model:value="formData.accountBalance" placeholder="请输入糕点" allow-clear :min="0" :max="99999999" style="width:100%"/>
+			</a-form-item>
+			<a-form-item label="积分:" name="voucherBalance">
+				<a-input-number v-model:value="formData.voucherBalance" placeholder="请输入积分" allow-clear :min="0" :max="99999999" style="width:100%"/>
+			</a-form-item>
+		</a-form>
+		<template #footer>
+			<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+			<a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup name="consumptionRecordForm">
+	import { cloneDeep } from 'lodash-es'
+	import { required } from '@/utils/formRules'
+	import bizUserApi from '@/api/biz/bizUserApi'
+	// 抽屉状态
+	const open = ref(false)
+	const emit = defineEmits({ successful: null })
+	const formRef = ref()
+	// 表单数据
+	const formData = ref({})
+	const submitLoading = ref(false)
+
+	const labelCol = ref({ span: 4 })
+	const wrapperCol = ref({ span: 18 })
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		open.value = true
+		if (record) {
+			let recordData = cloneDeep(record)
+			formData.value = Object.assign({}, recordData)
+		}
+	}
+
+	const onBlur = () => {
+		console.log(formData.value.phone)
+		formData.value.account = formData.value.phone
+		formData.value.orgId = '1543842934270394368'
+		formData.value.userType = '3'
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		formData.value = {}
+		open.value = false
+	}
+	// 默认要校验的
+	const formRules = {
+		name: [required('请输入姓名')],
+		phone: [required('请输入手机号')],
+		/*accountBalance: [required('请输入糕点')],
+		voucherBalance: [required('请输入积分')],*/
+	}
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				submitLoading.value = true
+				const formDataParam = cloneDeep(formData.value)
+				bizUserApi
+					.submitForm(formDataParam, formDataParam.id)
+					.then(() => {
+						onClose()
+						emit('successful')
+					})
+					.finally(() => {
+						submitLoading.value = false
+					})
+			})
+			.catch(() => {})
+	}
+	// 抛出函数
+	defineExpose({
+		onOpen
+	})
+</script>

+ 7 - 7
snowy-admin-web/src/views/biz/member/consumption.vue

@@ -10,14 +10,14 @@
 			<a-form-item label="会员姓名:" name="name">
 				<a-input v-model:value="formData.name" placeholder="请输入会员姓名" disabled allow-clear />
 			</a-form-item>
-			<a-form-item label="账户余额:" name="accountBalance">
+			<a-form-item label="糕点:" name="accountBalance">
 				<a-input v-model:value="formData.accountBalance" placeholder="请输入账户余额" disabled allow-clear />
 			</a-form-item>
-			<a-form-item label="代金券余额:" name="voucherBalance">
+			<a-form-item label="积分:" name="voucherBalance">
 				<a-input v-model:value="formData.voucherBalance" placeholder="请输入代金券余额" disabled allow-clear />
 			</a-form-item>
-			<a-form-item label="消费金额:" name="consumptionMoney">
-				<a-input-number v-model:value="formData.consumptionMoney" placeholder="请输入消费金额" allow-clear :min="0.1" :max="formData.accountBalance+formData.voucherBalance" style="width:100%"/>
+			<a-form-item label="消费糕点或积分:" name="consumptionMoney">
+				<a-input-number v-model:value="formData.consumptionMoney" placeholder="请输入消费糕点或积分" allow-clear :min="0.1" :max="formData.accountBalance+formData.voucherBalance" style="width:100%"/>
 			</a-form-item>
 			<a-form-item label="手机号:" name="phoneNumber">
 				<a-input v-model:value="formData.phoneNumber" placeholder="请输入手机号" disabled allow-clear style="width:100%"/>
@@ -85,7 +85,7 @@
 	// 默认要校验的
 	const formRules = {
 		name: [required('请输入消费者姓名')],
-		consumptionMoney: [required('请输入消费金额')],
+		consumptionMoney: [required('请输入消费糕点或积分')],
 		accountBalance:  [required('请输入账户余额')],
 		voucherBalance:  [required('请输入代金券余额')],
 		phoneNumber:  [required('请输入手机号')],
@@ -119,7 +119,7 @@
 			message.error('请先输入手机号!')
 			return
 		}
-		//bizAuthApi.sendMessage({phone:formData.value.phoneNumber}).then((res)=>{
+		bizAuthApi.sendMessage({phone:formData.value.phoneNumber}).then((res)=>{
 			/*if(res.code == '200'){
 				message.success("发送成功")
 			}else{
@@ -138,7 +138,7 @@
 					countdown.value = 300;
 				}
 			}, 1000);
-		//})
+		})
 	}
 	// 抛出函数
 	defineExpose({

+ 104 - 0
snowy-admin-web/src/views/biz/member/disable.vue

@@ -0,0 +1,104 @@
+<template>
+	<xn-form-container
+		title="详情"
+		:width="1000"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+
+		<a-form ref="formRef" :model="formData" layout="vertical">
+			<s-table
+				ref="tableRef"
+				:columns="columns"
+				:data="loadData"
+				:alert="false"
+				:showPagination="true"
+				bordered
+				:row-key="(record) => record.id"
+			>
+				<template #bodyCell="{ column, record }">
+					<template v-if="column.dataIndex === 'couponCode'">
+
+					</template>
+				</template>
+			</s-table>
+
+		</a-form>
+	</xn-form-container>
+</template>
+
+<script setup name="messageDetail">
+	import bizDisableRecordApi from '@/api/biz/bizDisableRecordApi'
+	import {cloneDeep} from "lodash-es";
+	const receiveInfoList = ref([])
+	const emit = defineEmits({ successful: null })
+
+	// 默认是关闭状态
+	const visible = ref(false)
+	const formRef = ref()
+	// 表单数据
+	const formData = ref({})
+	const tableRef = ref()
+	const columns = [
+		{
+			title: '姓名',
+			dataIndex: 'userName',
+			align: 'center',
+		},
+		{
+			title: '手机号',
+			dataIndex: 'phone',
+			align: 'center',
+		},
+		{
+			title: '冻结糕点',
+			dataIndex: 'accountBalance',
+			align: 'center',
+		},
+		{
+			title: '冻结积分',
+			dataIndex: 'voucherBalance',
+			align: 'center',
+		},
+		{
+			title: '冻结蛋糕券',
+			dataIndex: 'couponCode',
+			align: 'center',
+			ellipsis: true,
+			width:200
+		},
+		{
+			title: '冻结时间',
+			dataIndex: 'createTime',
+			align: 'center',
+		},
+	]
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+		if (record) {
+			let recordData = cloneDeep(record)
+			formData.value = Object.assign({}, recordData)
+		}
+	}
+
+	const loadData = () => {
+		let param = {
+			userId: formData.value.id,
+		}
+		return bizDisableRecordApi.bizDisableRecordPage(param).then((res) => {
+			return res
+		})
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		receiveInfoList.value = []
+		visible.value = false
+		emit('successful')
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>

+ 53 - 12
snowy-admin-web/src/views/biz/member/form.vue

@@ -10,14 +10,14 @@
 			<a-form-item label="会员姓名:" name="name">
 				<a-input v-model:value="formData.name" placeholder="请输入会员姓名" disabled allow-clear />
 			</a-form-item>
-			<a-form-item label="账户余额:" name="accountBalance">
+			<a-form-item label="糕点:" name="accountBalance">
 				<a-input v-model:value="formData.accountBalance" placeholder="请输入账户余额" disabled allow-clear />
 			</a-form-item>
-			<a-form-item label="代金券余额:" name="voucherBalance">
+			<a-form-item label="积分:" name="voucherBalance">
 				<a-input v-model:value="formData.voucherBalance" placeholder="请输入代金券余额" disabled allow-clear />
 			</a-form-item>
-			<a-form-item label="调整方式:" name="consumptionOperate">
-				<a-radio-group button-style="solid" v-model:value="formData.consumptionOperate">
+			<a-form-item label="调整操作:" name="consumptionOperate">
+				<a-radio-group button-style="solid" @change="onOperateChange" v-model:value="formData.consumptionOperate">
 					<a-radio-button value="1">
 						赠送
 					</a-radio-button>
@@ -26,18 +26,31 @@
 					</a-radio-button>
 				</a-radio-group>
 			</a-form-item>
-			<a-form-item label="调整类型:" name="consumptionType">
+			<a-form-item label="调整类别:" name="adjustType">
+				<a-radio-group button-style="solid" v-model:value="formData.adjustType">
+					<a-radio-button value="1" v-show="chongzhiFlag">
+						充值
+					</a-radio-button>
+					<a-radio-button value="2" v-show="tuikuanFlag">
+						退款
+					</a-radio-button>
+					<a-radio-button value="3" v-show="qitaFlag">
+						其他
+					</a-radio-button>
+				</a-radio-group>
+			</a-form-item>
+			<a-form-item label="调整方式:" name="consumptionType">
 				<a-radio-group button-style="solid" v-model:value="formData.consumptionType">
 					<a-radio-button value="1">
-						代金券
+						积分
 					</a-radio-button>
 					<a-radio-button value="2">
-						余额
+						糕点
 					</a-radio-button>
 				</a-radio-group>
 			</a-form-item>
-			<a-form-item label="调整金额:" name="consumptionMoney">
-				<a-input-number v-model:value="formData.consumptionMoney" placeholder="请输入调整金额" allow-clear :min="0.1" :max="99999999" style="width:100%"/>
+			<a-form-item label="调整糕点或积分:" name="consumptionMoney">
+				<a-input-number v-model:value="formData.consumptionMoney" placeholder="请输入调整糕点或积分" allow-clear :min="0.1" :max="99999999" style="width:100%"/>
 			</a-form-item>
 			<a-form-item label="说明:" name="consumptionRemark">
 				<a-input v-model:value="formData.consumptionRemark" placeholder="请输入说明" allow-clear />
@@ -68,6 +81,10 @@
 	const labelCol = ref({ span: 5 })
 	const wrapperCol = ref({ span: 18 })
 
+	const chongzhiFlag = ref(false)
+	const tuikuanFlag = ref(false)
+	const qitaFlag = ref(false)
+
 	// 打开抽屉
 	const onOpen = (record) => {
 		open.value = true
@@ -78,8 +95,30 @@
 			formData.value.consumptionType = ref('1')
 			formData.value.userId = formData.value.id
 			formData.value.id = ''
+			formData.value.adjustType = ref('1')
+			chongzhiFlag.value = true
+			qitaFlag.value = true
+			tuikuanFlag.value = false
 		}
 	}
+
+	const onOperateChange = (e) => {
+		console.log("value:"+e.target.value)
+		if(e.target.value == '1'){
+			//选择了赠送,展示充值和其他
+			chongzhiFlag.value = true
+			qitaFlag.value = true
+			tuikuanFlag.value = false
+			formData.value.adjustType = ref('1')
+		}else{
+			//展示退款和其他
+			chongzhiFlag.value = false
+			tuikuanFlag.value = true
+			qitaFlag.value = true
+			formData.value.adjustType = ref('2')
+		}
+	}
+
 	// 关闭抽屉
 	const onClose = () => {
 		formRef.value.resetFields()
@@ -90,16 +129,18 @@
 	const formRules = {
 		name: [required('请输入消费者姓名')],
 		consumptionOperate:  [required('请选择调整操作')],
-		consumptionType: [required('调整方式')],
-		consumptionMoney: [required('请输入调整金额')],
+		consumptionType: [required('请选择调整方式')],
+		consumptionMoney: [required('请输入调整糕点或积分')],
 		consumptionRemark: [required('请输入说明')],
 		accountBalance:  [required('请输入账户余额')],
 		voucherBalance:  [required('请输入代金券余额')],
+		adjustType:  [required('请选择调整类别')],
 	}
 	// 验证并提交数据
+	//title: '确定要为'+formData.value.name+(formData.value.consumptionOperate=='1'?'赠送':'扣减')+(formData.value.consumptionType=='1'?'代金券':'余额')+formData.value.consumptionMoney+'元吗?',
 	const onSubmit = () => {
 		Modal.confirm({
-			title: '确定要为'+formData.value.name+(formData.value.consumptionOperate=='1'?'赠送':'扣减')+(formData.value.consumptionType=='1'?'代金券':'余额')+formData.value.consumptionMoney+'元吗?',
+			title: '确定要为'+formData.value.name+(formData.value.consumptionOperate=='1'?'赠送':'扣减')+formData.value.consumptionMoney+(formData.value.consumptionType=='1'?'积分':'糕点')+'吗?',
 			icon: createVNode(ExclamationCircleOutlined),
 			content: '',
 			onOk() {

+ 140 - 0
snowy-admin-web/src/views/biz/member/impExp.vue

@@ -0,0 +1,140 @@
+<template>
+	<xn-form-container :destroy-on-close="true" :visible="visible" :width="700" title="导入" @close="onClose">
+		<span
+			>导入数据格式严格按照系统模板进行数据录入,请点击
+			<a-button size="small" type="primary" @click="downloadImportUserTemplate">下载模板</a-button>
+		</span>
+		<a-divider dashed />
+		<div>
+			<a-spin :spinning="impUploadLoading">
+				<a-upload-dragger :accept="uploadAccept" :custom-request="customRequestLocal" :show-upload-list="false">
+					<p class="ant-upload-drag-icon">
+						<inbox-outlined></inbox-outlined>
+					</p>
+					<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
+					<p class="ant-upload-hint">仅支持xls、xlsx格式文件</p>
+				</a-upload-dragger>
+			</a-spin>
+		</div>
+		 <a-alert v-if="impAlertStatus" :show-icon="false" banner class="mt-3" closable type="info" @close="onImpClose">
+			<template #description>
+				<p>导入总数:{{ impResultData.totalCount }} 条</p>
+				<p>导入成功:{{ impResultData.successCount }} 条</p>
+				<div v-if="impResultData.errorCount > 0">
+					<p><span style="color: red">失败条数:</span>{{ impResultData.errorCount }} 条</p>
+					<a-table :columns="impErrorColumns" :dataSource="impResultErrorDataSource" size="small" />
+				</div>
+			</template>
+		</a-alert>
+	</xn-form-container>
+</template>
+
+<script name="itemImpExp" setup>
+	import { message } from 'ant-design-vue'
+	import downloadUtil from '@/utils/downloadUtil'
+	import bizUserApi from '@/api/biz/bizUserApi'
+
+	const impUploadLoading = ref(false)
+	const impAlertStatus = ref(false)
+	const impResultData = ref({})
+	const impResultErrorDataSource = ref([])
+	const impAccept = [
+		{
+			extension: '.xls',
+			mimeType: 'application/vnd.ms-excel'
+		},
+		{
+			extension: '.xlsx',
+			mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+		}
+	]
+	// 指定能选择的文件类型
+	const uploadAccept = String(
+		impAccept.map((item) => {
+			return item.mimeType
+		})
+	)
+	// 导入
+	const customRequestLocal = (data) => {
+		impUploadLoading.value = true
+		const fileData = new FormData()
+		// 校验上传文件扩展名和文件类型是否为.xls、.xlsx
+		const extension = '.'.concat(data.file.name.split('.').slice(-1).toString().toLowerCase())
+		const mimeType = data.file.type
+		// 提取允许的扩展名
+		const extensionArr = impAccept.map((item) => item.extension)
+		// 提取允许的MIMEType
+		const mimeTypeArr = impAccept.map((item) => item.mimeType)
+		if (!extensionArr.includes(extension) || !mimeTypeArr.includes(mimeType)) {
+			message.warning('上传文件类型仅支持xls、xlsx格式文件!')
+			impUploadLoading.value = false
+			return false
+		}
+		fileData.append('file', data.file)
+		return bizUserApi
+			.itemImport(fileData)
+			.then((res) => {
+				impAlertStatus.value = true
+				impResultData.value = res
+				impResultErrorDataSource.value = res.errorDetail
+				emit('successful')
+			})
+			.finally(() => {
+				impUploadLoading.value = false
+			})
+	}
+	// 关闭导入提示
+	const onImpClose = () => {
+		console.log("关闭导入提示")
+		impAlertStatus.value = false
+	}
+	const impErrorColumns = [
+		{
+			title: '索引',
+			dataIndex: 'index',
+			width: '80px'
+		},
+		{
+			title: '原因',
+			dataIndex: 'msg'
+		}
+	]
+	// 定义emit事件
+	//const emit = defineEmits({ successful: null })
+	const emit = defineEmits(['itemImportBack'])
+	// 默认是关闭状态
+	let visible = ref(false)
+	const submitLoading = ref(false)
+
+	// 打开抽屉
+	const onOpen = () => {
+		visible.value = true
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		console.log("close")
+		visible.value = false
+		if (impAlertStatus.value === true) {
+			console.log("111")
+		}
+		emit('successful')
+		// 关闭导入的提示
+		onImpClose()
+	}
+	// 下载用户导入模板
+	const downloadImportUserTemplate = () => {
+		impUploadLoading.value = true
+		bizUserApi
+			.downloadImportTemplate()
+			.then((res) => {
+				downloadUtil.resultDownload(res)
+			})
+			.finally(() => {
+				impUploadLoading.value = false
+			})
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>

+ 66 - 11
snowy-admin-web/src/views/biz/member/index.vue

@@ -4,7 +4,7 @@
 			<a-row :gutter="24">
 				<a-col :span="6">
 					<a-form-item name="searchKey" :label="$t('common.searchKey')">
-						<a-input v-model:value="searchFormState.searchKey" :placeholder="'请输入姓名或号'" />
+						<a-input v-model:value="searchFormState.searchKey" :placeholder="'请输入姓名或手机号'" />
 					</a-form-item>
 				</a-col>
 				<a-col :span="6">
@@ -35,6 +35,18 @@
 	</a-card>
 	<a-card :bordered="false">
 		<s-table ref="tableRef" :columns="columns" :data="loadData" bordered :row-key="(record) => record.id">
+			<template #operator class="table-operator">
+				<a-space>
+					<a-button type="primary" @click="addUserRef.onOpen()" v-if="hasPerm('customerAdd')">
+						<template #icon><plus-outlined /></template>
+						新增
+					</a-button>
+					<a-button @click="ImpExpRef.onOpen()" style="margin-left:10px" v-if="hasPerm('customerImport')">
+						<template #icon><ImportOutlined /></template>
+						导入
+					</a-button>
+				</a-space>
+			</template>
 			<template #bodyCell="{ column, record, index }">
 				<template v-if="column.dataIndex === 'serial'">
 					{{ index + 1 }}
@@ -45,28 +57,47 @@
 					</span>
 				</template>
 				<template v-if="column.dataIndex === 'userStatus'">
-					<a-tag :color="record.userStatus === 'ENABLE' ? 'blue' : 'pink'">{{
-						$TOOL.dictTypeData('COMMON_STATUS', record.userStatus)
-					}}</a-tag>
+					<a-switch
+						:loading="loading"
+						:checked="record.userStatus === 'ENABLE'"
+						@change="editStatus(record)"
+						v-if="hasPerm('bizUserUpdataStatus')"
+					/>
+					<a-tag v-else :color="record.userStatus === 'ENABLE' ? 'blue' : 'pink'">{{
+							$TOOL.dictTypeData('COMMON_STATUS', record.userStatus)
+						}}</a-tag>
+<!--					<span v-else>{{ $TOOL.dictTypeData('COMMON_STATUS', record.userStatus) }}</span>-->
 				</template>
 				<template v-if="column.dataIndex === 'action'">
-					<a @click="formRef.onOpen(record)" v-if="hasPerm('bizUserEdit')">余额调整</a>
-					<a-divider type="vertical" v-if="hasPerm('bizUserConsumption')"/>
-					<a @click="consumptionRef.onOpen(record)" v-if="hasPerm('bizUserConsumption')">消费结算</a>
+					<a @click="formRef.onOpen(record)" v-if="hasPerm('bizUserEdit') && record.userStatus == 'ENABLE'">余额调整</a>
+					<a-divider type="vertical" v-if="hasPerm('bizUserConsumption') && record.userStatus == 'ENABLE'"/>
+					<a @click="consumptionRef.onOpen(record)" v-if="hasPerm('bizUserConsumption') && record.userStatus == 'ENABLE'">消费结算</a>
+<!--					<a-divider type="vertical" v-if="hasPerm('disableRecord') && record.userStatus == 'DISABLED'"/>-->
+					<a @click="disableRef.onOpen(record)" v-if="hasPerm('disableRecord') && record.userStatus == 'DISABLED'">冻结记录</a>
 				</template>
 			</template>
 		</s-table>
 	</a-card>
 	<Form ref="formRef" @successful="tableRef.refresh()" />
 	<Consumption ref="consumptionRef" @successful="tableRef.refresh()" />
+	<AddUserRef ref="addUserRef" @successful="tableRef.refresh()" />
+	<ImpExp ref="ImpExpRef" @successful="tableRef.refresh()" />
+	<DisableRef ref="disableRef" @successful="tableRef.refresh()" />
 </template>
 <script setup name="bizUser">
 	import tool from '@/utils/tool'
 	import bizUserApi from '@/api/biz/bizUserApi'
 	import Form from "./form.vue";
 	import Consumption from './consumption.vue'
+	import AddUserRef from './addUser.vue'
+	import ImpExp from './impExp.vue'
+	import DisableRef from './disable.vue'
 
 	const consumptionRef = ref()
+	const loading = ref(false)
+	const addUserRef = ref()
+	const ImpExpRef = ref()
+	const disableRef = ref()
 
 	const columns = [
 		{
@@ -88,12 +119,12 @@
 			width: 120,
 			align: 'center',
 		},
-		{
+		/*{
 			title: '性别',
 			dataIndex: 'gender',
 			align: 'center',
 			width: 80
-		},
+		},*/
 		{
 			title: '手机',
 			dataIndex: 'phone',
@@ -102,13 +133,13 @@
 			ellipsis: true
 		},
 		{
-			title: '账户余额',
+			title: '糕点',
 			dataIndex: 'accountBalance',
 			width: 80,
 			align: 'center',
 		},
 		{
-			title: '代金券余额',
+			title: '积分',
 			dataIndex: 'voucherBalance',
 			width: 80,
 			align: 'center',
@@ -144,6 +175,30 @@
 		searchFormRef.value.resetFields()
 		tableRef.value.refresh(true)
 	}
+
+	// 修改状态
+	const editStatus = (record) => {
+		loading.value = true
+		if (record.userStatus === 'ENABLE') {
+			bizUserApi
+				.userDisableCustomer(record)
+				.then(() => {
+					tableRef.value.refresh()
+				})
+				.finally(() => {
+					loading.value = false
+				})
+		} else {
+			bizUserApi
+				.userEnableUser(record)
+				.then(() => {
+					tableRef.value.refresh()
+				})
+				.finally(() => {
+					loading.value = false
+				})
+		}
+	}
 </script>
 
 <style scoped>

+ 3 - 3
snowy-admin-web/src/views/biz/org/index.vue

@@ -128,12 +128,12 @@
 		{
 			title: '门店编码',
 			dataIndex: 'code',
-			width:150
+			width:120
 		},
 		{
 			title: '门店地址',
 			dataIndex: 'address',
-			width:200
+			width:180
 		},
 		{
 			title: '分类',
@@ -151,7 +151,7 @@
 			title: '操作',
 			dataIndex: 'action',
 			align: 'center',
-			width: 150
+			width: 180
 		})
 	}
 	const selectedRowKeys = ref([])

+ 17 - 9
snowy-admin-web/src/views/biz/rebaterecord/index.vue

@@ -61,35 +61,43 @@
 		{
 			title: '序号',
 			dataIndex: 'serial',
-			width: 80
+			width: 80,
+			align: 'center',
 		},
 		{
 			title: '充值人',
-			dataIndex: 'rechargeUserName'
+			dataIndex: 'rechargeUserName',
+			align: 'center',
 		},
 		{
 			title: '充值金额(元)',
-			dataIndex: 'rechargeAmount'
+			dataIndex: 'rechargeAmount',
+			align: 'center',
 		},
 		{
 			title: '推荐人',
-			dataIndex: 'recommendUserName'
+			dataIndex: 'recommendUserName',
+			align: 'center',
 		},
 		{
-			title: '返利金额(元)',
-			dataIndex: 'rebateAmout'
+			title: '返利积分(个)',
+			dataIndex: 'rebateAmout',
+			align: 'center',
 		},
 		{
 			title: '返利比例',
-			dataIndex: 'rebateRatio'
+			dataIndex: 'rebateRatio',
+			align: 'center',
 		},
 		{
 			title: '充值时间',
-			dataIndex: 'rechargeTime'
+			dataIndex: 'rechargeTime',
+			align: 'center',
 		},
 		{
 			title: '状态',
-			dataIndex: 'rebateStatus'
+			dataIndex: 'rebateStatus',
+			align: 'center',
 		}
 	]
 	// // 操作栏通过权限判断是否显示

+ 75 - 10
snowy-admin-web/src/views/biz/rechargeplanconfig/form.vue

@@ -7,25 +7,57 @@
 		@close="onClose"
 	>
 		<a-form ref="formRef" :model="formData" :rules="formRules" :label-col="{span: 5}">
+			<a-form-item label="充值门店" name="orgId">
+				<a-tree-select
+					v-model:value="formData.orgId"
+					style="width: 100%"
+					:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
+					placeholder="请选择充值门店"
+					allow-clear
+					tree-default-expand-all
+					:tree-data="treeData"
+					:field-names="{
+									children: 'children',
+									label: 'name',
+									value: 'id'
+								}"
+					selectable="false"
+					tree-line
+				/>
+			</a-form-item>
 			<a-form-item label="充值金额及以上:" name="rechargeAmount">
 				<a-input-number v-model:value="formData.rechargeAmount" style="width: 100%" :min="0" :max="99999999"
 								:precision="2" placeholder="请输入充值金额及以上" allow-clear/>
 			</a-form-item>
-			<a-form-item label="优惠券金额:" name="couponAmount">
+			<a-form-item label="蛋糕券金额:" name="couponAmount">
 				<a-input-number v-model:value="formData.couponAmount" style="width: 100%" :min="0" :max="99999999"
 								:precision="2" placeholder="请输入优惠券金额" allow-clear/>
 			</a-form-item>
-			<a-form-item label="优惠券数量:" name="couponNum">
+			<a-form-item label="蛋糕券数量:" name="couponNum">
 				<a-input-number v-model:value="formData.couponNum" style="width: 100%" :min="0" :max="99999999"
 								:precision="0" placeholder="请输入优惠券数量" allow-clear/>
 			</a-form-item>
-			<a-form-item label="账户到账金额:" name="accountBalance">
+			<a-form-item label="账户到账糕点:" name="accountBalance">
 				<a-input-number v-model:value="formData.accountBalance" style="width: 100%" :min="0" :max="99999999"
-								:precision="2" placeholder="请输入账户到账金额" allow-clear/>
+								:precision="2" placeholder="请输入账户到账糕点" allow-clear/>
+			</a-form-item>
+			<a-form-item label="是否立即到账:" name="isAccount" >
+				<a-radio-group button-style="solid" v-model:value="formData.isAccount" @change="onOperateChange">
+					<a-radio-button value="1">
+						是
+					</a-radio-button>
+					<a-radio-button value="0">
+						否
+					</a-radio-button>
+				</a-radio-group>
 			</a-form-item>
-			<a-form-item label="返点比例(代金券):" name="rebateRatio">
+			<a-form-item label="帮助激活人数:" name="helpCount" v-show="helpFlag" :rules="helpRules">
+				<a-input-number v-model:value="formData.helpCount" style="width: 100%" :min="1" :max="99999999"
+								:precision="0" placeholder="请输入帮助激活糕点到账人数" allow-clear/>
+			</a-form-item>
+			<a-form-item label="返点比例(积分):" name="rebateRatio">
 				<a-input-number v-model:value="formData.rebateRatio" style="width: 100%" :min="0" :max="100"
-								:precision="2" placeholder="请输入返点比例(代金券)" addon-after="%" allow-clear/>
+								:precision="2" placeholder="请输入返点比例(积分)" addon-after="%" allow-clear/>
 			</a-form-item>
 		</a-form>
 		<template #footer>
@@ -39,6 +71,7 @@
 import {cloneDeep} from 'lodash-es'
 import {required} from '@/utils/formRules'
 import bizRechargePlanConfigApi from '@/api/biz/bizRechargePlanConfigApi'
+import bizOrgApi from '@/api/biz/bizOrgApi'
 // 抽屉状态
 const open = ref(false)
 const emit = defineEmits({successful: null})
@@ -46,13 +79,25 @@ const formRef = ref()
 // 表单数据
 const formData = ref({})
 const submitLoading = ref(false)
+const treeData = ref([])
+const helpFlag = ref(false)
+const helpRules = ref([])
 
 // 打开抽屉
 const onOpen = (record) => {
 	open.value = true
+	formData.value.isAccount = ref('1')
 	if (record) {
 		let recordData = cloneDeep(record)
 		formData.value = Object.assign({}, recordData)
+		if(formData.value.isAccount == '1'){
+			helpFlag.value = false
+			helpRules.value = []
+		}else{
+			helpFlag.value = true
+			helpRules.value.push({required:true,message: '请输入帮助激活糕点到账人数'})
+		}
+
 	}
 }
 // 关闭抽屉
@@ -61,13 +106,20 @@ const onClose = () => {
 	formData.value = {}
 	open.value = false
 }
+
+// 获取机构树并加入顶级
+bizOrgApi.orgTreeSelector().then((res) => {
+	treeData.value = res
+})
 // 默认要校验的
 const formRules = {
 	rechargeAmount: [required('请输入充值金额及以上')],
-	couponAmount: [required('请输入优惠券金额')],
-	couponNum: [required('请输入优惠券数量')],
-	accountBalance: [required('请输入账户到账金额')],
-	rebateRatio: [required('请输入返点比例(代金券)')]
+	couponAmount: [required('请输入蛋糕券金额')],
+	couponNum: [required('请输入蛋糕券数量')],
+	accountBalance: [required('请输入账户到账糕点')],
+	rebateRatio: [required('请输入返点比例(积分)')],
+	orgId: [required('请选择充值门店')],
+	isAccount: [required('请选择是否立即到账')],
 }
 // 验证并提交数据
 const onSubmit = () => {
@@ -89,6 +141,19 @@ const onSubmit = () => {
 		.catch(() => {
 		})
 }
+//选择是否立即到账
+const onOperateChange = (e) => {
+	console.log("value:"+e.target.value)
+	if(e.target.value == '1'){
+		//选择了是
+		helpFlag.value = false
+		helpRules.value=[]
+	}else{
+		//选择了否
+		helpFlag.value = true
+		helpRules.value.push({required:true,message: '请输入帮助激活糕点到账人数'})
+	}
+}
 // 抛出函数
 defineExpose({
 	onOpen

+ 68 - 11
snowy-admin-web/src/views/biz/rechargeplanconfig/index.vue

@@ -2,6 +2,26 @@
 	<a-card :bordered="false" class="xn-mb10">
 		<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
 			<a-row :gutter="24">
+				<a-col :span="6">
+					<a-form-item label="门店" name="orgId">
+						<a-tree-select
+							v-model:value="searchFormState.orgId"
+							style="width: 100%"
+							:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
+							placeholder="请选择门店"
+							allow-clear
+							tree-default-expand-all
+							:tree-data="treeData"
+							:field-names="{
+									children: 'children',
+									label: 'name',
+									value: 'id'
+								}"
+							selectable="false"
+							tree-line
+						/>
+					</a-form-item>
+				</a-col>
 				<a-col :span="6">
 					<a-form-item name="status" label="状态">
 						<a-select v-model:value="searchFormState.status" placeholder="请选择">
@@ -58,6 +78,19 @@
 					/>
 					<span v-else>{{ $TOOL.dictTypeData('COMMON_STATUS', record.userStatus) }}</span>
 				</template>
+				<template v-if="column.dataIndex === 'isAccount'">
+					<a-tag
+						:color="
+							record.isAccount === '0'
+								? '#87d068'
+								: record.isAccount === '1'
+								  ? '#f50'
+								      : '#f50'
+						"
+					>
+						{{ $TOOL.dictTypeData('is_destroy', record.isAccount) }}
+					</a-tag>
+				</template>
 				<template v-if="column.dataIndex === 'action'">
 					<a-space>
 						<a
@@ -92,6 +125,7 @@
 <script setup name="rechargeplanconfig">
 	import Form from './form.vue'
 	import bizRechargePlanConfigApi from '@/api/biz/bizRechargePlanConfigApi'
+	import bizOrgApi from '@/api/biz/bizOrgApi'
 	import tool from '@/utils/tool'
 	import { ref } from 'vue'
 	const searchFormRef = ref()
@@ -100,35 +134,53 @@
 	const formRef = ref()
 	const loading = ref(false)
 	const statusData = tool.dictList('COMMON_STATUS')
+	const treeData = ref([])
 	const columns = [
 		{
 			title: '序号',
 			dataIndex: 'serial',
-			width: 80
+			width: 80,
+			align: 'center',
+		},
+		{
+			title: '门店',
+			dataIndex: 'orgName',
+			align: 'center',
 		},
 		{
 			title: '充值金额及以上(元)',
-			dataIndex: 'rechargeAmount'
+			dataIndex: 'rechargeAmount',
+			align: 'center',
 		},
 		{
-			title: '优惠券金额(元)',
-			dataIndex: 'couponAmount'
+			title: '蛋糕券金额(元)',
+			dataIndex: 'couponAmount',
+			align: 'center',
 		},
 		{
-			title: '优惠券数量',
-			dataIndex: 'couponNum'
+			title: '蛋糕券数量',
+			dataIndex: 'couponNum',
+			align: 'center',
 		},
 		{
-			title: '账户增加金额(元)',
-			dataIndex: 'accountBalance'
+			title: '账户到账糕点(个)',
+			dataIndex: 'accountBalance',
+			align: 'center',
 		},
 		{
-			title: '返点比例(代金券)',
-			dataIndex: 'rebateRatio'
+			title: '是否立即到账',
+			dataIndex: 'isAccount',
+			align: 'center',
+		},
+		{
+			title: '返点比例(积分)',
+			dataIndex: 'rebateRatio',
+			align: 'center',
 		},
 		{
 			title: '状态',
-			dataIndex: 'status'
+			dataIndex: 'status',
+			align: 'center',
 		}
 	]
 	// 操作栏通过权限判断是否显示
@@ -152,6 +204,11 @@
 		searchFormRef.value.resetFields()
 		tableRef.value.refresh(true)
 	}
+
+	// 获取机构树并加入顶级
+	bizOrgApi.orgTreeSelector().then((res) => {
+		treeData.value = res
+	})
 	// 删除
 	const deleteBizRechargePlanConfig = (record) => {
 		let params = [

+ 5 - 5
snowy-admin-web/src/views/biz/rechargerecord/form.vue

@@ -3,11 +3,11 @@
 		<a-form ref="formRef" :model="formData">
 			<a-descriptions :column="2" bordered>
 				<a-descriptions-item label="姓名">{{ formData.name }}</a-descriptions-item>
-				<a-descriptions-item label="充值金额">{{ formData.rechargeAmount }}</a-descriptions-item>
-				<a-descriptions-item label="充值金额">{{ formData.rechargeAmount }}</a-descriptions-item>
+				<a-descriptions-item label="手机号">{{ formData.phone }}</a-descriptions-item>
+				<a-descriptions-item label="充值金额">{{ formData.rechargeAmount + '元' }}</a-descriptions-item>
 				<a-descriptions-item label="充值时间">{{ formData.rechargeTime }}</a-descriptions-item>
-				<a-descriptions-item label="原账户余额">{{ formData.oldAccountBalance + '元' }}</a-descriptions-item>
-				<a-descriptions-item label="新账户余额">{{ formData.newAccountBalance + '元' }}</a-descriptions-item>
+				<a-descriptions-item label="原账户糕点">{{ formData.oldAccountBalance + '个' }}</a-descriptions-item>
+				<a-descriptions-item label="新账户糕点">{{ formData.newAccountBalance + '个' }}</a-descriptions-item>
 				<a-descriptions-item label="返利金额" v-if="formData.rebateAmount != null">{{
 					formData.rebateAmount
 				}}</a-descriptions-item>
@@ -15,7 +15,7 @@
 					formData.couponNum
 				}}</a-descriptions-item>
 				<a-descriptions-item label="订单号">{{ formData.orderNo }}</a-descriptions-item>
-				<a-descriptions-item label="是否支付">{{ formData.isPay }}</a-descriptions-item>
+				<a-descriptions-item label="是否支付">{{ formData.isPay == true?'已支付':'待支付' }}</a-descriptions-item>
 				<a-descriptions-item label="充值方案说明:" v-if="formData.rechargePlanId != null">{{
 					formData.rechargePlanDescribe
 				}}</a-descriptions-item>

+ 21 - 13
snowy-admin-web/src/views/biz/rechargerecord/index.vue

@@ -4,7 +4,7 @@
 			<a-row :gutter="24">
 				<a-col :span="6">
 					<a-form-item label="关键字搜素" name="searchKey">
-						<a-input v-model:value="searchFormState.userId" placeholder="请输入姓名或者手机号" />
+						<a-input v-model:value="searchFormState.searchKey" placeholder="请输入姓名或者手机号" />
 					</a-form-item>
 				</a-col>
 				<a-col :span="6">
@@ -44,7 +44,7 @@
 	import { cloneDeep } from 'lodash-es'
 	import Form from './form.vue'
 	import bizRechargeRecordApi from '@/api/biz/bizRechargeRecordApi'
-	const searchFormState = ref({})
+	let searchFormState = reactive({})
 	const searchFormRef = ref()
 	const tableRef = ref()
 	const formRef = ref()
@@ -52,35 +52,43 @@
 		{
 			title: '序号',
 			dataIndex: 'serial',
-			width: 80
+			width: 80,
+			align: 'center',
 		},
 		{
 			title: '姓名',
-			dataIndex: 'name'
+			dataIndex: 'name',
+			align: 'center',
 		},
 		{
 			title: '手机号',
-			dataIndex: 'phone'
+			dataIndex: 'phone',
+			align: 'center',
 		},
 		{
 			title: '充值金额(元)',
-			dataIndex: 'rechargeAmount'
+			dataIndex: 'rechargeAmount',
+			align: 'center',
 		},
 		{
 			title: '充值时间',
-			dataIndex: 'rechargeTime'
+			dataIndex: 'rechargeTime',
+			align: 'center',
 		},
 		{
-			title: '原账户余额(元)',
-			dataIndex: 'oldAccountBalance'
+			title: '原账户糕点(个)',
+			dataIndex: 'oldAccountBalance',
+			align: 'center',
 		},
 		{
-			title: '新账户余额(元)',
-			dataIndex: 'newAccountBalance'
+			title: '新账户糕点(个)',
+			dataIndex: 'newAccountBalance',
+			align: 'center',
 		},
 		{
 			title: '是否支付',
-			dataIndex: 'isPay'
+			dataIndex: 'isPay',
+			align: 'center',
 		}
 	]
 	// 操作栏通过权限判断是否显示
@@ -91,7 +99,7 @@
 		width: 150
 	})
 	const loadData = (parameter) => {
-		const searchFormParam = cloneDeep(searchFormState.value)
+		const searchFormParam = JSON.parse(JSON.stringify(searchFormState))
 		// rechargeTime范围查询条件重载
 		if (searchFormParam.rechargeTime) {
 			searchFormParam.startRechargeTime = searchFormParam.rechargeTime[0]

+ 18 - 4
snowy-admin-web/src/views/biz/recommendrecord/index.vue

@@ -44,19 +44,33 @@
 		{
 			title: '序号',
 			dataIndex: 'serial',
-			width: 80
+			width: 80,
+			align: 'center'
 		},
 		{
 			title: '推荐人',
-			dataIndex: 'recommendUserName'
+			dataIndex: 'recommendUserName',
+			align: 'center'
+		},
+		{
+			title: '推荐人手机号',
+			dataIndex: 'recommendUserPhone',
+			align: 'center'
 		},
 		{
 			title: '被推荐人',
-			dataIndex: 'acceptUserName'
+			dataIndex: 'acceptUserName',
+			align: 'center'
+		},
+		{
+			title: '被推荐人手机号',
+			dataIndex: 'acceptUserPhone',
+			align: 'center'
 		},
 		{
 			title: '推荐时间',
-			dataIndex: 'createTime'
+			dataIndex: 'createTime',
+			align: 'center'
 		}
 	]
 	const loadData = (parameter) => {

+ 822 - 0
snowy-admin-web/src/views/biz/statisty/total.vue

@@ -0,0 +1,822 @@
+<template>
+	<a-card :bordered="false" :body-style="{ 'padding-bottom': '20px' }" class="mb-2">
+		<a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
+			<a-row :gutter="24">
+				<a-col :span="6">
+					<a-form-item label="门店" name="orgId">
+						<a-tree-select
+							v-model:value="searchFormState.orgId"
+							show-search
+							style="width: 100%"
+							:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
+							placeholder="请选择门店"
+							allow-clear
+							multiple
+							tree-default-expand-all
+							:tree-data="treeData"
+							:field-names="{
+								children: 'children',
+								label: 'name',
+								value: 'id'
+							}"
+							selectable="false"
+
+						/>
+					</a-form-item>
+				</a-col>
+				<a-col :span="6">
+					<a-form-item label="日期段" name="times">
+						<a-range-picker
+							style="width: 100%;"
+							v-model:value="searchFormState.times"
+
+							format="YYYY-MM-DD"
+							:placeholder="['开始时间', '结束时间']"
+							@change="onRangeChange"
+						/>
+					</a-form-item>
+				</a-col>
+
+				<a-col :span="6">
+					<a-button type="primary" html-type="submit" @click="refresh()">查询</a-button>
+					<a-button style="margin: 0 8px" @click="reset">重置</a-button>
+					<a-button @click="exportTotal">
+						<template #icon>
+							<export-outlined/>
+						</template>
+						导出
+					</a-button>
+				</a-col>
+			</a-row>
+		</a-form>
+	</a-card>
+	<a-row>
+		<a-col :span="8" style="margin-bottom: 10px;">
+			<a-card style="height: 180px">
+				<a-statistic title="消费糕点或积分" :value="consumerCount"/>
+				<a-statistic title="订单数" :value="consumerOrderCount"/>
+			</a-card>
+		</a-col>
+		<a-col :span="8" style="margin-bottom: 10px; margin-left: 20px">
+			<a-card style="height: 180px">
+				<a-statistic title="注册会员数" :value="userCount"/>
+<!--				<a-statistic title="订单数" :value="rechargeOrderCount"/>-->
+			</a-card>
+		</a-col>
+		<a-col :span="7" style="margin-bottom: 10px; margin-left: 20px;">
+			<a-card style="height: 180px">
+				<a-statistic title="账户糕点" :value="accountBalance"/>
+				<a-statistic title="账户积分" :value="voucherBalance"/>
+			</a-card>
+		</a-col>
+	</a-row>
+
+	<a-card :bordered="false">
+
+		<div style="display:flex;justify-content:space-between" id="totalDiv">
+			<span id="totalSpan">
+				<left-outlined :style="{fontSize:'13px',color:'#C9C9C9',cursor:'pointer'}" @click="prePage" v-show="leftFlag"/>
+			</span>
+			<span>
+				<right-outlined style="width:1.5em;height:1.5em;color:#C9C9C9;cursor:pointer" @click="nextPage" v-show="rightFlag"/>
+			</span>
+		</div>
+
+
+	</a-card>
+
+<!--	<a-card :bordered="false" :body-style="{ 'padding-bottom': '20px' }" class="mb-2">
+		<div id="main" style="height: 340px"></div>
+	</a-card>-->
+
+	<a-card :bordered="false" :body-style="{ 'padding-bottom': '20px' }" class="mb-2">
+		<div id="main2" style="height: 340px"></div>
+	</a-card>
+
+	<a-card :bordered="false" :body-style="{ 'padding-bottom': '20px' }" class="mb-2">
+
+		<a-table
+		ref="table"
+		:columns="columns4"
+		:data-source = "data4"
+		bordered
+		:row-key="(record) => record.id"
+
+		>
+		</a-table>
+
+	</a-card>
+
+
+	<a-card :bordered="false">
+
+			<a-tabs v-model:activeKey="activeKey" @change="clickTab(value)">
+				<a-tab-pane key="1">
+					<template #tab>
+						<span>
+						<red-envelope-outlined />
+						消费列表
+						</span>
+					</template>
+					<div id="printForm1">
+						<a-table
+							ref="table"
+							:columns="columns1"
+							:data-source = "data1"
+							bordered
+							:row-key="(record) => record.id"
+
+						>
+						<template #bodyCell="{ column, record }">
+							<template v-if="column.dataIndex === 'consumptionOperate'">
+								<a-tag
+									:color="
+							record.consumptionOperate === '1'
+								? 'orange'
+								: record.consumptionOperate === '2'
+								  ? 'green'
+								  : record.consumptionOperate === '3'
+								    ? 'cyan'
+								      : 'purple'
+						"
+								>
+									{{ $TOOL.dictTypeData('consumption_operate', record.consumptionOperate) }}
+								</a-tag>
+							</template>
+						</template>
+						</a-table>
+					</div>
+				</a-tab-pane>
+				<a-tab-pane key="2">
+					<template #tab>
+						<span>
+						<HddOutlined />
+						优惠券列表
+						</span>
+					</template>
+					<div id="printForm2">
+						<a-table
+							ref="table"
+							:columns="columns2"
+							:data-source = "data2"
+							bordered
+							:row-key="(record) => record.id"
+
+						>
+							<template #bodyCell="{ column, record }">
+								<template v-if="column.dataIndex === 'couponStatus'">
+									<a-tag
+										:color="
+							record.couponStatus === '0'
+								? '#87d068'
+								: record.couponStatus === '1'
+								  ? '#f50'
+								      : '#f50'
+						"
+									>
+										{{ $TOOL.dictTypeData('is_destroy', record.couponStatus) }}
+									</a-tag>
+								</template>
+							</template>
+						</a-table>
+					</div>
+				</a-tab-pane>
+				<a-tab-pane key="3">
+					<template #tab>
+						<span>
+						<ScheduleOutlined />
+						会员列表
+						</span>
+					</template>
+					<div id="printForm3">
+						<a-table
+							ref="table"
+							:columns="columns3"
+							:data-source = "data3"
+							bordered
+							:row-key="(record) => record.id"
+
+						>
+						</a-table>
+					</div>
+				</a-tab-pane>
+
+			</a-tabs>
+
+	</a-card>
+
+</template>
+
+<script setup name="customerinfo">
+	import bizOrgApi from '@/api/biz/bizOrgApi'
+	import downloadUtil from '@/utils/downloadUtil'
+	import consumptionRecordApi from '@/api/biz/consumptionRecordApi'
+	import bizCouponRecordApi from '@/api/biz/bizCouponRecordApi'
+	import bizUserApi from '@/api/biz/bizUserApi'
+	import { onMounted } from 'vue'
+	import orgApi from '@/api/biz/bizOrgApi'
+	import * as echarts from 'echarts'
+
+	//消费
+	const consumerCount = ref(0)
+	const consumerOrderCount = ref(0)
+	//充值统计数量
+	const rechargeCount = ref(0)
+	const rechargeOrderCount = ref(0)
+	//会员
+	const userCount = ref(0)
+	//账户余额&代金券余额
+	const accountBalance = ref(0)
+	const voucherBalance = ref(0)
+
+	const treeData = ref([])
+	const activeKey = ref('1');
+
+	const searchFormRef = ref()
+	let searchFormState = reactive({})
+
+	const data1 = ref([])
+	const data2 = ref([])
+	const data3 = ref([])
+	const data4 = ref([])
+	const data5 = ref([])
+
+	const dayTime = ref()
+	const zhibiao = ref()
+	const fuhe = ref()
+	const typeTitle = ref()
+
+	//三条数据一页
+	const echartsPage = ref(7)
+	//当前显示页数
+	const currentPage = ref(1)
+	// 共有多少页
+	const totalPage = ref(0)
+
+	const leftFlag = ref(false)
+	const rightFlag = ref(false)
+
+
+
+
+	onMounted(() => {
+		//金额汇总
+		loadData1();
+		loadData3();
+		clickTab()
+		// 获取机构树并加入顶级
+		bizOrgApi.orgTreeSelector().then((res) => {
+			treeData.value = res
+		})
+		//折线图
+		customer();
+		//各个门店消费统计
+		loadData4()
+	})
+
+	const onRangeChange = (value, dateString) => {
+		console.log('Formatted Selected Time: ', dateString);
+		searchFormState.startTime = dateString[0]
+		searchFormState.endTime = dateString[1]
+		delete searchFormState.times
+	};
+
+	const table = ref()
+	const formRef = ref()
+	const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+	const columns1 = [
+		{
+			title: '会员姓名',
+			dataIndex: 'userName',
+			align: 'center',
+		},
+		{
+			title: '会员手机号',
+			dataIndex: 'phone',
+			align: 'center',
+		},
+		{
+			title: '消费操作',
+			dataIndex: 'consumptionOperate',
+			align: 'center'
+		},
+		{
+			title: '消费金额',
+			dataIndex: 'consumptionMoney',
+			align: 'center'
+		},
+		{
+			title: '账户糕点',
+			dataIndex: 'newAccountBalance',
+			align: 'center'
+		},
+		{
+			title: '账户积分',
+			dataIndex: 'newVoucherBalance',
+			align: 'center'
+		},
+		{
+			title: '消费时间',
+			dataIndex: 'consumptionTime',
+			align: 'center'
+		},
+		{
+			title: '会员编码',
+			dataIndex: 'userCode',
+			align: 'center'
+		},
+		{
+			title: '消费门店',
+			dataIndex: 'orgName',
+			align: 'center'
+		},
+	]
+
+	const columns2 = [
+		{
+			title: '优惠券编码',
+			dataIndex: 'couponNo',
+			align: 'center',
+		},
+		{
+			title: '优惠券生成时间',
+			dataIndex: 'time',
+			align: 'center',
+		},
+		{
+			title: '是否核销',
+			dataIndex: 'couponStatus',
+			align: 'center'
+		},
+		{
+			title: '有效期开始时间',
+			dataIndex: 'startTime',
+			align: 'center'
+		},
+		{
+			title: '有效期截止时间',
+			dataIndex: 'endTime',
+			align: 'center'
+		},
+		{
+			title: '核销人',
+			dataIndex: 'destroyUserName',
+			align: 'center'
+		},
+		{
+			title: '核销时间',
+			dataIndex: 'destroyTime',
+			align: 'center'
+		},
+		{
+			title: '核销门店',
+			dataIndex: 'destroyOrgName',
+			align: 'center'
+		},
+	]
+
+
+	const columns3 = [
+		{
+			title: '会员编码',
+			dataIndex: 'userReferralCode',
+			align: 'center',
+		},
+		{
+			title: '手机号',
+			dataIndex: 'phone',
+			align: 'center',
+		},
+		{
+			title: '姓名',
+			dataIndex: 'name',
+			align: 'center',
+		},
+		{
+			title: '账户糕点',
+			dataIndex: 'accountBalance',
+			align: 'center',
+		},
+		{
+			title: '账户积分',
+			dataIndex: 'voucherBalance',
+			align: 'center',
+		},
+		{
+			title: '注册时间',
+			dataIndex: 'createTime',
+			align: 'center',
+		},
+		/*{
+			title: '门店',
+			dataIndex: 'orgName',
+			align: 'center',
+		},*/
+	]
+
+
+	const columns4 = [
+		{
+			title: '门店名称',
+			dataIndex: 'name',
+			align: 'center'
+		},
+		{
+			title: '门店编码',
+			dataIndex: 'code',
+			align: 'center'
+		},
+		{
+			title: '消费',
+			children: [
+				{
+					title: '订单数',
+					dataIndex: 'orderCount',
+					align: 'center',
+				},
+				{
+					title: '账户消费糕点',
+					dataIndex: 'accountMoney',
+					align: 'center',
+				},
+				{
+					title: '账户消费积分',
+					dataIndex: 'voucherMoney',
+					align: 'center',
+				},
+			]
+		},
+		/*{
+			title: '注册会员数',
+			dataIndex: 'userCount',
+			align: 'center'
+		},*/
+
+	]
+
+
+	const selectedRowKeys = ref([])
+	// 列表选择配置
+	const options = {
+		// columns数字类型字段加入 needTotal: true 可以勾选自动算账
+		alert: {
+			show: true,
+			clear: () => {
+				selectedRowKeys.value = ref([])
+			}
+		},
+		rowSelection: {
+			onChange: (selectedRowKey, selectedRows) => {
+				selectedRowKeys.value = selectedRowKey
+			}
+		}
+	}
+
+	const totalMoney = ref(0)
+
+	//各个门店消费记录
+	const loadData4 = () => {
+		const orgId = ref('')
+		if(searchFormState.orgId == null || searchFormState.orgId== '' || searchFormState.orgId== 'undefined' ){
+
+		}else{
+			for(let i = 0;i<searchFormState.orgId.length;i++){
+				orgId.value = orgId.value + searchFormState.orgId[i]+","
+			}
+			orgId.value = orgId.value.slice(0,orgId.value.length-1)
+		}
+		const param = {
+			"orgId":orgId.value,
+			"consumptionTimeBegin":searchFormState.startTime,
+			"consumptionTimeEnd":searchFormState.endTime
+		}
+		consumptionRecordApi.queryEachStore(param).then((data)=>{
+			data4.value = data.dataList
+		})
+	}
+
+
+	//导出
+	const exportTotal = () => {
+		const orgId = ref('')
+		if(searchFormState.orgId == null || searchFormState.orgId== '' || searchFormState.orgId== 'undefined' ){
+
+		}else{
+			for(let i = 0;i<searchFormState.orgId.length;i++){
+				orgId.value = orgId.value + searchFormState.orgId[i]+","
+			}
+			orgId.value = orgId.value.slice(0,orgId.value.length-1)
+		}
+		const param = {
+			"orgId":orgId.value,
+			"consumptionTimeBegin":searchFormState.startTime,
+			"consumptionTimeEnd":searchFormState.endTime
+		}
+		consumptionRecordApi.exportRecord(param).then((res)=>{
+			downloadUtil.resultDownload(res)
+		})
+	}
+
+	//会员折线图
+	const customer = () => {
+		const orgId = ref('')
+		if(searchFormState.orgId == null || searchFormState.orgId== '' || searchFormState.orgId== 'undefined' ){
+
+		}else{
+			for(let i = 0;i<searchFormState.orgId.length;i++){
+				orgId.value = orgId.value + searchFormState.orgId[i]+","
+			}
+			orgId.value = orgId.value.slice(0,orgId.value.length-1)
+		}
+		const param = {
+			"orgId":orgId.value,
+			"customerName":searchFormState.customerName,
+			"startTime":searchFormState.startTime,
+			"endTime":searchFormState.endTime
+		}
+		consumptionRecordApi.queryConsumptionChart(param).then((res)=>{
+			const xAxis = res.dateTime
+
+			let series = [
+				{
+					name: '消费糕点或积分',
+					type: 'line',
+					//stack: '总量',
+					showSymbol: false,
+					itemStyle: {
+					normal: {
+						color: '#00FF00', //折线点自定义
+						lineStyle: {
+						color: '#00FF00'
+						}
+					}
+					},
+					data: res.consumptionMoney
+				},
+				{
+					name: '订单数',
+					type: 'line',
+					//stack: '总量',
+					showSymbol: false,
+					data: res.consumptionCount
+				},
+				{
+					name: '会员',
+					type: 'line',
+					//stack: '总量',
+					showSymbol: false,
+					itemStyle: {
+						normal: {
+							color: '#FAD337', //折线点自定义
+							lineStyle: {
+							color: '#FAD337'
+							}
+						}
+					},
+					data: res.userCount
+				},
+
+			]
+			drow("main2",xAxis, series)
+		})
+	}
+
+	const drow = (id, xAxis, series) => {
+	let myChart = echarts.init(document.getElementById(id))
+
+	let option = {
+		color: ['#1890FF', '#52C41A'],
+		title: {
+		//text: '会话量',
+		},
+		tooltip: {
+		trigger: 'axis',
+		},
+		grid: {
+		left: '40px',
+		right: '40px',
+		top: '50px',
+		bottom: '30px'
+		},
+		legend: {
+		itemWidth: 20, // 图例图形宽度
+		itemHeight: 4,
+		icon: 'roundRect',
+		},
+		xAxis: {
+		type: 'category',
+		boundaryGap: false, //x轴两边不留空白
+		axisLabel: {
+			color: 'rgba(0, 0, 0, 0.65)',
+		},
+		axisLine: {
+			lineStyle: {
+			color: '#D9D9D9',
+			},
+		},
+		axisTick: {
+			lineStyle: {
+			color: '#D9D9D9',
+			},
+		},
+		data: xAxis,
+		},
+		yAxis: {
+		axisLine: {
+			show: false,
+			lineStyle: {
+			color: 'rgba(0, 0, 0, 0.65)',
+			},
+		},
+		splitLine: {
+			lineStyle: {
+			color: ['#E8E8E8'],
+			type: 'dashed',
+			},
+		},
+		axisLabel: {
+			color: 'rgba(0, 0, 0, 0.65)',
+		},
+		axisTick: {
+			show: false,
+			lineStyle: { color: 'rgb(150,150,150)' }, //y轴坐标刻度颜色(与axisLabel.textStyle是相同的效果)
+		},
+		type: 'value',
+		},
+		series: series
+	}
+	myChart.setOption(option)
+	}
+
+	//消费金额值统计
+	const loadData1 = () => {
+		const orgId = ref('')
+		if(searchFormState.orgId == null || searchFormState.orgId== '' || searchFormState.orgId== 'undefined' ){
+
+		}else{
+			for(let i = 0;i<searchFormState.orgId.length;i++){
+				orgId.value = orgId.value + searchFormState.orgId[i]+","
+			}
+			orgId.value = orgId.value.slice(0,orgId.value.length-1)
+		}
+		const param = {
+			"orgId":orgId.value,
+			"consumptionTimeBegin":searchFormState.startTime,
+			"consumptionTimeEnd":searchFormState.endTime
+		}
+		consumptionRecordApi.queryRecordTotal(param).then((data) => {
+			consumerCount.value = '¥ ' + data.orderMoney
+			consumerOrderCount.value = data.orderCount
+		})
+	}
+
+	//账户余额值统计
+	const loadData3 = () => {
+		const orgId = ref('')
+		if(searchFormState.orgId == null || searchFormState.orgId== '' || searchFormState.orgId== 'undefined' ){
+
+		}else{
+			for(let i = 0;i<searchFormState.orgId.length;i++){
+				orgId.value = orgId.value + searchFormState.orgId[i]+","
+			}
+			orgId.value = orgId.value.slice(0,orgId.value.length-1)
+		}
+		const param = {
+			"orgId":orgId.value,
+			"consumptionTimeBegin":searchFormState.startTime,
+			"consumptionTimeEnd":searchFormState.endTime
+		}
+		consumptionRecordApi.queryBalanceTotal(param).then((data)=>{
+			userCount.value = data.userCount
+			accountBalance.value = data.accountBalance
+			voucherBalance.value = data.voucherBalance
+		})
+	}
+
+
+	//切换
+	const clickTab = () => {
+		consumptionPage()
+		userPage()
+		couponPage()
+	}
+
+	const consumptionPage = () =>{
+		const orgId = ref('')
+		if(searchFormState.orgId == null || searchFormState.orgId== '' || searchFormState.orgId== 'undefined' ){
+
+		}else{
+			for(let i = 0;i<searchFormState.orgId.length;i++){
+				orgId.value = orgId.value + searchFormState.orgId[i]+","
+			}
+			orgId.value = orgId.value.slice(0,orgId.value.length-1)
+		}
+		const param = {
+			"orgId":orgId.value,
+			"consumptionTimeBegin":searchFormState.startTime,
+			"consumptionTimeEnd":searchFormState.endTime,
+			"consumptionOperate":'3,4'
+		}
+		consumptionRecordApi.consumptionRecordPage(param).then((data) => {
+			data1.value = data.records
+		})
+	}
+
+	//会员列表
+	const userPage = () => {
+		const orgId = ref('')
+		if(searchFormState.orgId == null || searchFormState.orgId== '' || searchFormState.orgId== 'undefined' ){
+
+		}else{
+			for(let i = 0;i<searchFormState.orgId.length;i++){
+				orgId.value = orgId.value + searchFormState.orgId[i]+","
+			}
+			orgId.value = orgId.value.slice(0,orgId.value.length-1)
+		}
+		const param = {
+			"orgId":orgId.value,
+			"beginTime":searchFormState.startTime,
+			"endTime":searchFormState.endTime
+		}
+		bizUserApi.memberPage(param).then((res) => {
+			data3.value = res.records
+		})
+	}
+
+
+	//优惠券列表
+	const couponPage = () => {
+		const orgId = ref('')
+		if(searchFormState.orgId == null || searchFormState.orgId== '' || searchFormState.orgId== 'undefined' ){
+
+		}else{
+			for(let i = 0;i<searchFormState.orgId.length;i++){
+				orgId.value = orgId.value + searchFormState.orgId[i]+","
+			}
+			orgId.value = orgId.value.slice(0,orgId.value.length-1)
+		}
+		const param = {
+			"orgId":orgId.value,
+			"beginTime":searchFormState.startTime,
+			"endTime":searchFormState.endTime
+		}
+		bizCouponRecordApi.bizCouponRecordPage(param).then((data) => {
+			data2.value = data.records
+		})
+	}
+
+
+	//查询
+	const refresh = () => {
+		loadData1();
+		loadData3();
+		clickTab()
+		loadData4()
+	}
+
+	//导出
+	const exportReport = () => {
+		if(activeKey.value == '1'){
+			searchFormState.queryFlag = '1'
+			console.log("searchFormState.queryFlag:"+searchFormState.queryFlag)
+			reportApi.exportReport(searchFormState).then((res) => {
+				downloadUtil.resultDownload(res)
+			})
+
+		}
+	}
+
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		searchFormState.times = []
+		searchFormState.startTime = null
+		searchFormState.endTime = null
+		searchFormState.orgId = []
+	}
+
+</script>
+
+<style lang="less" scoped>
+.ant-table-thead > tr > th {
+    text-align: center;
+}
+
+.dashboard-analysis-iconGroup {
+  i {
+    margin-left: 16px;
+    color: rgba(0, 0, 0, .45);
+    cursor: pointer;
+    transition: color .32s;
+    color: black;
+	height: 20px;
+  }
+}
+
+#totalDiv #totalSpan{
+	svg{
+		width: 1.5em;
+		height: 1.5em;
+	}
+}
+</style>

+ 122 - 0
snowy-admin-web/src/views/biz/warn/detail.vue

@@ -0,0 +1,122 @@
+<template>
+	<xn-form-container
+		title="详情"
+		:width="1000"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+
+		<a-form ref="formRef" :model="formData" layout="vertical">
+			<s-table
+				ref="tableRef"
+				:columns="columns"
+				:data="loadData"
+				:alert="false"
+				:showPagination="true"
+				bordered
+				:row-key="(record) => record.id"
+			>
+				<template #bodyCell="{ column, record }">
+					<template v-if="column.dataIndex === 'adjustType'">
+						<a-tag
+							:color="
+							record.adjustType === '1'
+								? 'orange'
+								: record.adjustType === '2'
+								  ? 'green'
+								  : record.adjustType === '3'
+								    ? 'cyan'
+								      : 'purple'
+						"
+						>
+							{{ $TOOL.dictTypeData('adjust_type', record.adjustType) }}
+						</a-tag>
+					</template>
+				</template>
+			</s-table>
+
+		</a-form>
+	</xn-form-container>
+</template>
+
+<script setup name="messageDetail">
+	import consumptionRecordApi from '@/api/biz/consumptionRecordApi'
+	import bizUserApi from "@/api/biz/bizUserApi";
+	import {cloneDeep} from "lodash-es";
+	const receiveInfoList = ref([])
+	const emit = defineEmits({ successful: null })
+
+	// 默认是关闭状态
+	const visible = ref(false)
+	const formRef = ref()
+	// 表单数据
+	const formData = ref({})
+	const tableRef = ref()
+	const columns = [
+		{
+			title: '姓名',
+			dataIndex: 'userName',
+			align: 'center',
+		},
+		{
+			title: '手机号',
+			dataIndex: 'phone',
+			align: 'center',
+		},
+		{
+			title: '调整操作',
+			dataIndex: 'adjustType',
+			align: 'center',
+		},
+		{
+			title: '退款金额',
+			dataIndex: 'consumptionMoney',
+			align: 'center',
+		},
+		{
+			title: '退款日期',
+			dataIndex: 'consumptionTime',
+			align: 'center',
+		},
+		{
+			title: '退款门店',
+			dataIndex: 'orgName',
+			align: 'center',
+		},
+		{
+			title: '说明',
+			dataIndex: 'consumptionRemark',
+			align: 'center',
+		},
+	]
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+		if (record) {
+			let recordData = cloneDeep(record)
+			formData.value = Object.assign({}, recordData)
+		}
+	}
+
+	const loadData = () => {
+		let param = {
+			recordId: formData.value.id,
+			consumptionOperate:'2',
+			adjustType:'2'
+		}
+		return consumptionRecordApi.consumptionRecordPage(param).then((res) => {
+			return res
+		})
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		receiveInfoList.value = []
+		visible.value = false
+		emit('successful')
+	}
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>

+ 232 - 0
snowy-admin-web/src/views/biz/warn/index.vue

@@ -0,0 +1,232 @@
+<template>
+	<a-card :bordered="false" style="margin-bottom: 10px" class="mb-2">
+		<a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
+			<a-row :gutter="24">
+				<a-col :span="6">
+					<a-form-item label="姓名" name="userName">
+						<a-input v-model:value="searchFormState.userName" placeholder="姓名查询" allow-clear />
+					</a-form-item>
+				</a-col>
+				<a-col :span="6">
+					<a-button type="primary" html-type="submit" @click="tableRef.refresh()">查询</a-button>
+					<a-button style="margin: 0 8px" @click="reset">重置</a-button>
+<!--					<a @click="toggleAdvanced" style="margin-left: 8px">
+						{{ advanced ? '收起' : '展开' }}
+						<component :is="advanced ? 'up-outlined' : 'down-outlined'" />
+					</a>-->
+				</a-col>
+			</a-row>
+		</a-form>
+	</a-card>
+	<a-card :bordered="false">
+		<s-table
+			ref="tableRef"
+			:columns="columns"
+			:data="loadData"
+			bordered
+			:row-key="(record) => record.id"
+		>
+			<template #operator class="table-operator">
+				<a-space>
+					<a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('consumptionRecordAdd')">
+						<template #icon><plus-outlined /></template>
+						新增
+					</a-button>
+				</a-space>
+			</template>
+			<template #bodyCell="{ column, record,index }">
+				<template v-if="column.dataIndex === 'action'">
+					<a-space>
+						<a @click="detailForm.onOpen(record)" v-if="hasPerm('warnDetail')">详情</a>
+					</a-space>
+				</template>
+				<template v-if="column.dataIndex === 'consumptionOperate'">
+					<a-tag
+						:color="
+							record.consumptionOperate === '1'
+								? 'orange'
+								: record.consumptionOperate === '2'
+								  ? 'green'
+								  : record.consumptionOperate === '3'
+								    ? 'cyan'
+								      : 'purple'
+						"
+					>
+						{{ $TOOL.dictTypeData('consumption_operate', record.consumptionOperate) }}
+					</a-tag>
+				</template>
+				<template v-if="column.dataIndex === 'serial'">
+					{{ index + 1 }}
+				</template>
+				<template v-if="column.dataIndex === 'userStatus'">
+					<a-switch
+						:loading="loading"
+						:checked="record.userStatus === 'ENABLE'"
+						@change="editStatus(record)"
+						v-if="hasPerm('bizUserUpdataStatus')"
+					/>
+					<a-tag v-else :color="record.userStatus === 'ENABLE' ? 'blue' : 'pink'">{{
+							$TOOL.dictTypeData('COMMON_STATUS', record.userStatus)
+						}}</a-tag>
+					<!--					<span v-else>{{ $TOOL.dictTypeData('COMMON_STATUS', record.userStatus) }}</span>-->
+				</template>
+			</template>
+		</s-table>
+	</a-card>
+	<DetailForm ref="detailForm" @successful="tableRef.refresh()" />
+</template>
+
+<script setup name="consumptionrecord">
+	import { cloneDeep } from 'lodash-es'
+	import consumptionRecordApi from '@/api/biz/consumptionRecordApi'
+	import tool from '@/utils/tool'
+	import bizOrgApi from '@/api/biz/bizOrgApi'
+	import DetailForm from './detail.vue'
+	import Consumption from "@/views/biz/member/consumption.vue";
+	import bizUserApi from '@/api/biz/bizUserApi'
+
+	const tableRef = ref()
+	const formRef = ref()
+	const detailForm = ref()
+
+	const searchFormRef = ref()
+	let searchFormState = reactive({})
+	// 查询区域显示更多控制
+	const advanced = ref(false)
+	const toggleAdvanced = () => {
+		advanced.value = !advanced.value
+	}
+	const loading = ref(false)
+	const consumptionOperateList = tool.dictList('consumption_operate')
+	const treeData = ref([])
+
+	const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+	const columns = [
+		{
+			title: '序号',
+			dataIndex: 'serial',
+			align: 'center',
+			width: 50
+		},
+		{
+			title: '推荐人姓名',
+			dataIndex: 'name',
+			align: 'center',
+		},
+		{
+			title: '账号',
+			dataIndex: 'account',
+			align: 'center',
+		},
+		{
+			title: '手机号',
+			dataIndex: 'phone',
+			align: 'center',
+		},
+		{
+			title: '退款次数',
+			dataIndex: 'count',
+			align: 'center',
+		},
+		{
+			title: '状态',
+			dataIndex: 'userStatus',
+			align: 'center',
+		},
+
+		{
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+		},
+	]
+	// 操作栏通过权限判断是否显示
+	/*if (hasPerm(['consumptionRecordEdit', 'consumptionRecordDelete'])) {
+		columns.push({
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+			width: 150
+		})
+	}*/
+
+	// 获取机构树并加入顶级
+	bizOrgApi.orgTreeSelector().then((res) => {
+		treeData.value = res
+	})
+
+	const selectedRowKeys = ref([])
+	// 列表选择配置
+	const options = {
+		// columns数字类型字段加入 needTotal: true 可以勾选自动算账
+		alert: {
+			show: true,
+			clear: () => {
+				selectedRowKeys.value = ref([])
+			}
+		},
+		rowSelection: {
+			onChange: (selectedRowKey, selectedRows) => {
+				selectedRowKeys.value = selectedRowKey
+			}
+		}
+	}
+	const loadData = (parameter) => {
+		const searchFormParam = JSON.parse(JSON.stringify(searchFormState))
+		// planTime范围查询条件重载
+		if (searchFormParam.consumptionTime) {
+			searchFormParam.consumptionTimeBegin = searchFormParam.consumptionTime[0]
+			searchFormParam.consumptionTimeEnd = searchFormParam.consumptionTime[1]
+			delete searchFormParam.consumptionTime
+		}
+		return consumptionRecordApi.warnPage(Object.assign(parameter, searchFormParam)).then((data) => {
+			return data
+		})
+	}
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		tableRef.value.refresh(true)
+	}
+	// 删除
+	const deleteConsumptionRecord = (record) => {
+		let params = [
+			{
+				id: record.id
+			}
+		]
+		consumptionRecordApi.consumptionRecordDelete(params).then(() => {
+			tableRef.value.refresh(true)
+		})
+	}
+	// 批量删除
+	const deleteBatchConsumptionRecord = (params) => {
+		consumptionRecordApi.consumptionRecordDelete(params).then(() => {
+			tableRef.value.clearRefreshSelected()
+		})
+	}
+
+	// 修改状态
+	const editStatus = (record) => {
+		loading.value = true
+		if (record.userStatus === 'ENABLE') {
+			bizUserApi
+				.userDisableUser(record)
+				.then(() => {
+					tableRef.value.refresh()
+				})
+				.finally(() => {
+					loading.value = false
+				})
+		} else {
+			bizUserApi
+				.userEnableUser(record)
+				.then(() => {
+					tableRef.value.refresh()
+				})
+				.finally(() => {
+					loading.value = false
+				})
+		}
+	}
+</script>

+ 2 - 1
snowy-admin-web/src/views/dev/slideshow/index.vue

@@ -81,7 +81,7 @@
 					}}</a-tag>
 				</template>
 				<template v-if="column.dataIndex === 'image'">
-					<a-image :src="record.image" style="width: 50px; height: 30px" />
+					<a-image :src="sysConfig.API_URL+record.image" style="width: 50px; height: 30px" />
 				</template>
 				<template v-if="column.dataIndex === 'status'">
 					<a-switch
@@ -111,6 +111,7 @@
 	import { cloneDeep } from 'lodash-es'
 	import Form from './form.vue'
 	import slideshowApi from '@/api/dev/slideshowApi'
+	import sysConfig from "@/config";
 	const searchFormState = ref({})
 	const searchFormRef = ref()
 	const tableRef = ref()

+ 3 - 3
snowy-admin-web/src/views/sys/org/index.vue

@@ -123,12 +123,12 @@
 		{
 			title: '门店编码',
 			dataIndex: 'code',
-			width:150
+			width:120
 		},
 		{
 			title: '门店地址',
 			dataIndex: 'address',
-			width:200
+			width:180
 		},
 		{
 			title: '分类',
@@ -144,7 +144,7 @@
 			title: '操作',
 			dataIndex: 'action',
 			align: 'center',
-			width: 150
+			width: 180
 		}
 	]
 	const selectedRowKeys = ref([])

+ 1 - 1
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonCouponGeneratorUtil.java

@@ -24,7 +24,7 @@ public class CommonCouponGeneratorUtil {
             for (byte b : hash) {
                 sb.append(String.format("%02X", b));
             }
-            return "BBT-" + sb.substring(0, 12); // 取前 10 位
+            return "DGQ-" + sb.substring(0, 12); // 取前 10 位
         } catch (NoSuchAlgorithmException e) {
             throw new RuntimeException("MD5 算法不可用", e);
         }

+ 13 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/pojo/SaBaseLoginUser.java

@@ -17,6 +17,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
 
+import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
 
@@ -242,6 +243,18 @@ public abstract class SaBaseLoginUser {
     @Schema(description = "数据范围集合", hidden = true)
     private List<DataScope> dataScopeList;
 
+    /** 用户类型: 1. 管理员 2.门店 3.消费者*/
+    private String userType;
+
+    /**账户糕点*/
+    private BigDecimal accountBalance;
+
+    /**账户积分*/
+    private BigDecimal voucherBalance;
+
+    /**用户推荐码*/
+    private String userReferralCode;
+
     /** 用户密码hash值 */
     @JsonIgnore
     @Schema(description = "用户密码hash值")

+ 5 - 1
snowy-plugin/snowy-plugin-biz/pom.xml

@@ -38,5 +38,9 @@
             <groupId>vip.xiaonuo</groupId>
             <artifactId>snowy-plugin-dev-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+        </dependency>
     </dependencies>
-</project>
+</project>

+ 123 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/controller/BizActivateController.java

@@ -0,0 +1,123 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.biz.modular.bizactivate.entity.BizActivate;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateAddParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateEditParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateIdParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivatePageParam;
+import vip.xiaonuo.biz.modular.bizactivate.service.BizActivateService;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 用户充值待激活记录控制器
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ */
+@Tag(name = "用户充值待激活记录控制器")
+@RestController
+@Validated
+public class BizActivateController {
+
+    @Resource
+    private BizActivateService bizActivateService;
+
+    /**
+     * 获取用户充值待激活记录分页
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    @Operation(summary = "获取用户充值待激活记录分页")
+    @SaCheckPermission("/biz/bizactivate/page")
+    @GetMapping("/biz/bizactivate/page")
+    public CommonResult<Page<BizActivate>> page(BizActivatePageParam bizActivatePageParam) {
+        return CommonResult.data(bizActivateService.page(bizActivatePageParam));
+    }
+
+    /**
+     * 添加用户充值待激活记录
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    @Operation(summary = "添加用户充值待激活记录")
+    @CommonLog("添加用户充值待激活记录")
+    @SaCheckPermission("/biz/bizactivate/add")
+    @PostMapping("/biz/bizactivate/add")
+    public CommonResult<String> add(@RequestBody @Valid BizActivateAddParam bizActivateAddParam) {
+        bizActivateService.add(bizActivateAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑用户充值待激活记录
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    @Operation(summary = "编辑用户充值待激活记录")
+    @CommonLog("编辑用户充值待激活记录")
+    @SaCheckPermission("/biz/bizactivate/edit")
+    @PostMapping("/biz/bizactivate/edit")
+    public CommonResult<String> edit(@RequestBody @Valid BizActivateEditParam bizActivateEditParam) {
+        bizActivateService.edit(bizActivateEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除用户充值待激活记录
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    @Operation(summary = "删除用户充值待激活记录")
+    @CommonLog("删除用户充值待激活记录")
+    @SaCheckPermission("/biz/bizactivate/delete")
+    @PostMapping("/biz/bizactivate/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   List<BizActivateIdParam> bizActivateIdParamList) {
+        bizActivateService.delete(bizActivateIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取用户充值待激活记录详情
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    @Operation(summary = "获取用户充值待激活记录详情")
+    @SaCheckPermission("/biz/bizactivate/detail")
+    @GetMapping("/biz/bizactivate/detail")
+    public CommonResult<BizActivate> detail(@Valid BizActivateIdParam bizActivateIdParam) {
+        return CommonResult.data(bizActivateService.detail(bizActivateIdParam));
+    }
+}

+ 64 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/entity/BizActivate.java

@@ -0,0 +1,64 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import vip.xiaonuo.common.pojo.CommonEntity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 用户充值待激活记录实体
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+@Getter
+@Setter
+@TableName("biz_activate")
+public class BizActivate extends CommonEntity {
+
+    /** 主键ID */
+    @TableId
+    @Schema(description = "主键ID")
+    private String id;
+
+    /** 用户id */
+    @Schema(description = "用户id")
+    private String userId;
+
+    /** 账户糕点 */
+    @Schema(description = "账户糕点")
+    private BigDecimal accountBalance;
+
+    /** 状态1:待激活    2:已激活 */
+    @Schema(description = "状态1:待激活    2:已激活")
+    private String status;
+
+    /** 帮助激活人数 */
+    @Schema(description = "帮助激活人数")
+    private Integer helpCount;
+
+    /**充值方案id*/
+    private String planConfigId;
+
+    @TableField(exist = false)
+    private String userName;
+
+    /**成功激活需要人数*/
+    private Integer successCount;
+}

+ 34 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/enums/BizActivateEnum.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.enums;
+
+import lombok.Getter;
+
+/**
+ * 用户充值待激活记录枚举
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+@Getter
+public enum BizActivateEnum {
+
+    /** 测试 */
+    TEST("TEST");
+
+    private final String value;
+
+    BizActivateEnum(String value) {
+        this.value = value;
+    }
+}

+ 29 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/mapper/BizActivateMapper.java

@@ -0,0 +1,29 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import vip.xiaonuo.biz.modular.bizactivate.entity.BizActivate;
+
+/**
+ * 用户充值待激活记录Mapper接口
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+public interface BizActivateMapper extends BaseMapper<BizActivate> {
+    Page<BizActivate> getPageList(@Param("page") Page<BizActivate> page, @Param("ew") QueryWrapper<BizActivate> ew) ;
+}

+ 18 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/mapper/mapping/BizActivateMapper.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="vip.xiaonuo.biz.modular.bizactivate.mapper.BizActivateMapper">
+
+    <select id="getPageList" resultType="vip.xiaonuo.biz.modular.bizactivate.entity.BizActivate">
+        select
+            su.name userName,
+            ba.account_balance,
+            ba.status,
+            ba.help_count,
+            ba.create_time,
+            brpc.help_count successCount
+        from biz_activate ba
+        left join SYS_USER su on su.id = ba.user_id
+        left join biz_recharge_plan_config brpc on brpc.id = ba.plan_config_id
+        ${ew.customSqlSegment}
+    </select>
+</mapper>

+ 50 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/param/BizActivateAddParam.java

@@ -0,0 +1,50 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 用户充值待激活记录添加参数
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+@Getter
+@Setter
+public class BizActivateAddParam {
+
+    /** 用户id */
+    @Schema(description = "用户id")
+    private String userId;
+
+    /** 账户糕点 */
+    @Schema(description = "账户糕点")
+    private BigDecimal accountBalance;
+
+    /** 状态1:待激活    2:已激活 */
+    @Schema(description = "状态1:待激活    2:已激活")
+    private String status;
+
+    /** 帮助激活人数 */
+    @Schema(description = "帮助激活人数")
+    private Integer helpCount;
+
+}

+ 55 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/param/BizActivateEditParam.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 用户充值待激活记录编辑参数
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+@Getter
+@Setter
+public class BizActivateEditParam {
+
+    /** 主键ID */
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "id不能为空")
+    private String id;
+
+    /** 用户id */
+    @Schema(description = "用户id")
+    private String userId;
+
+    /** 账户糕点 */
+    @Schema(description = "账户糕点")
+    private BigDecimal accountBalance;
+
+    /** 状态1:待激活    2:已激活 */
+    @Schema(description = "状态1:待激活    2:已激活")
+    private String status;
+
+    /** 帮助激活人数 */
+    @Schema(description = "帮助激活人数")
+    private Integer helpCount;
+
+}

+ 35 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/param/BizActivateIdParam.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 用户充值待激活记录Id参数
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+@Getter
+@Setter
+public class BizActivateIdParam {
+
+    /** 主键ID */
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "id不能为空")
+    private String id;
+}

+ 55 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/param/BizActivatePageParam.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 用户充值待激活记录查询参数
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+@Getter
+@Setter
+public class BizActivatePageParam {
+
+    /** 当前页 */
+    @Schema(description = "当前页码")
+    private Integer current;
+
+    /** 每页条数 */
+    @Schema(description = "每页条数")
+    private Integer size;
+
+    /** 排序字段 */
+    @Schema(description = "排序字段,字段驼峰名称,如:userName")
+    private String sortField;
+
+    /** 排序方式 */
+    @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND")
+    private String sortOrder;
+
+    /** 关键词 */
+    @Schema(description = "关键词")
+    private String searchKey;
+
+    private String userName;
+
+    private String status;
+
+}

+ 80 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/service/BizActivateService.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import vip.xiaonuo.biz.modular.bizactivate.entity.BizActivate;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateAddParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateEditParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateIdParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivatePageParam;
+
+import java.util.List;
+
+/**
+ * 用户充值待激活记录Service接口
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+public interface BizActivateService extends IService<BizActivate> {
+
+    /**
+     * 获取用户充值待激活记录分页
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    Page<BizActivate> page(BizActivatePageParam bizActivatePageParam);
+
+    /**
+     * 添加用户充值待激活记录
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    void add(BizActivateAddParam bizActivateAddParam);
+
+    /**
+     * 编辑用户充值待激活记录
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    void edit(BizActivateEditParam bizActivateEditParam);
+
+    /**
+     * 删除用户充值待激活记录
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    void delete(List<BizActivateIdParam> bizActivateIdParamList);
+
+    /**
+     * 获取用户充值待激活记录详情
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     */
+    BizActivate detail(BizActivateIdParam bizActivateIdParam);
+
+    /**
+     * 获取用户充值待激活记录详情
+     *
+     * @author fanzherong
+     * @date  2025/02/27 16:36
+     **/
+    BizActivate queryEntity(String id);
+}

+ 94 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/bizactivate/service/impl/BizActivateServiceImpl.java

@@ -0,0 +1,94 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.bizactivate.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import vip.xiaonuo.common.enums.CommonSortOrderEnum;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.page.CommonPageRequest;
+import vip.xiaonuo.biz.modular.bizactivate.entity.BizActivate;
+import vip.xiaonuo.biz.modular.bizactivate.mapper.BizActivateMapper;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateAddParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateEditParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivateIdParam;
+import vip.xiaonuo.biz.modular.bizactivate.param.BizActivatePageParam;
+import vip.xiaonuo.biz.modular.bizactivate.service.BizActivateService;
+
+import java.util.List;
+
+/**
+ * 用户充值待激活记录Service接口实现类
+ *
+ * @author fanzherong
+ * @date  2025/02/27 16:36
+ **/
+@Service
+public class BizActivateServiceImpl extends ServiceImpl<BizActivateMapper, BizActivate> implements BizActivateService {
+
+    @Override
+    public Page<BizActivate> page(BizActivatePageParam bizActivatePageParam) {
+        QueryWrapper<BizActivate> queryWrapper = new QueryWrapper<BizActivate>().checkSqlInjection();
+        if(ObjectUtil.isNotEmpty(bizActivatePageParam.getUserName())){
+            queryWrapper.like("su.name",bizActivatePageParam.getUserName());
+        }
+        if(ObjectUtil.isNotEmpty(bizActivatePageParam.getStatus())){
+            queryWrapper.eq("ba.status",bizActivatePageParam.getStatus());
+        }
+        queryWrapper.orderByDesc("ba.create_time");
+        return this.getBaseMapper().getPageList(CommonPageRequest.defaultPage(), queryWrapper);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void add(BizActivateAddParam bizActivateAddParam) {
+        BizActivate bizActivate = BeanUtil.toBean(bizActivateAddParam, BizActivate.class);
+        this.save(bizActivate);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void edit(BizActivateEditParam bizActivateEditParam) {
+        BizActivate bizActivate = this.queryEntity(bizActivateEditParam.getId());
+        BeanUtil.copyProperties(bizActivateEditParam, bizActivate);
+        this.updateById(bizActivate);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void delete(List<BizActivateIdParam> bizActivateIdParamList) {
+        // 执行删除
+        this.removeByIds(CollStreamUtil.toList(bizActivateIdParamList, BizActivateIdParam::getId));
+    }
+
+    @Override
+    public BizActivate detail(BizActivateIdParam bizActivateIdParam) {
+        return this.queryEntity(bizActivateIdParam.getId());
+    }
+
+    @Override
+    public BizActivate queryEntity(String id) {
+        BizActivate bizActivate = this.getById(id);
+        if(ObjectUtil.isEmpty(bizActivate)) {
+            throw new CommonException("用户充值待激活记录不存在,id值为:{}", id);
+        }
+        return bizActivate;
+    }
+}

+ 81 - 1
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/controller/ConsumptionRecordController.java

@@ -14,14 +14,18 @@ package vip.xiaonuo.biz.modular.consumptionrecord.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.http.MediaType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
 import vip.xiaonuo.biz.modular.consumptionrecord.param.*;
+import vip.xiaonuo.biz.modular.user.result.BizMemberUserResult;
 import vip.xiaonuo.common.annotation.CommonLog;
 import vip.xiaonuo.common.annotation.CommonNoRepeat;
 import vip.xiaonuo.common.pojo.CommonResult;
@@ -31,7 +35,10 @@ import vip.xiaonuo.biz.modular.consumptionrecord.service.ConsumptionRecordServic
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotEmpty;
+
+import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 消费记录控制器
@@ -120,10 +127,83 @@ public class ConsumptionRecordController {
         return CommonResult.data(consumptionRecordService.detail(consumptionRecordIdParam));
     }
 
-    @Operation(summary = "门店每日销售额统计")
+    @Operation(summary = "小程序门店每日销售额统计")
     @SaCheckPermission("/biz/consumptionrecord/getRecordTotal")
     @GetMapping("/biz/consumptionrecord/getRecordTotal")
     public CommonResult<ConsumptionResult> getRecordTotal() {
         return CommonResult.data(consumptionRecordService.getRecordTotal());
     }
+
+    @Operation(summary = "小程序门店每日销售额统计及较昨日对比")
+    @SaCheckPermission("/biz/consumptionrecord/queryTodayRevenue")
+    @GetMapping("/biz/consumptionrecord/queryTodayRevenue")
+    public CommonResult<Map<String,Object>> queryTodayRevenue(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        return CommonResult.data(consumptionRecordService.queryTodayRevenue(consumptionRecordPageParam));
+    }
+
+    /**
+     * 后台销售额统计
+     * @return
+     */
+    @Operation(summary = "后台销售额统计")
+    @SaCheckPermission("/biz/consumptionrecord/queryRecordTotal")
+    @GetMapping("/biz/consumptionrecord/queryRecordTotal")
+    public CommonResult<ConsumptionResult> getRecordTotal(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        return CommonResult.data(consumptionRecordService.getRecordTotal(consumptionRecordPageParam));
+    }
+
+    /**
+     * 后台账户余额&会员数统计
+     * @return
+     */
+    @Operation(summary = "后台账户余额&会员数统计")
+    @SaCheckPermission("/biz/consumptionrecord/queryBalanceTotal")
+    @GetMapping("/biz/consumptionrecord/queryBalanceTotal")
+    public CommonResult<UserBalanceResult> queryBalanceTotal(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        return CommonResult.data(consumptionRecordService.queryBalanceTotal(consumptionRecordPageParam));
+    }
+
+    /**
+     * 后台消费金额订单数折线图
+     * @return
+     */
+    @Operation(summary = "后台消费金额订单数折线图")
+    @SaCheckPermission("/biz/consumptionrecord/queryConsumptionChart")
+    @GetMapping("/biz/consumptionrecord/queryConsumptionChart")
+    public CommonResult<Map<String,Object>> queryConsumptionChart(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        return CommonResult.data(consumptionRecordService.queryConsumptionChart(consumptionRecordPageParam));
+    }
+
+    /**
+     * 各个门店消费记录
+     * @return
+     */
+    @Operation(summary = "各个门店消费记录")
+    @SaCheckPermission("/biz/consumptionrecord/queryEachStore")
+    @GetMapping("/biz/consumptionrecord/queryEachStore")
+    public CommonResult<Map<String,Object>> queryEachStore(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        return CommonResult.data(consumptionRecordService.queryEachStore(consumptionRecordPageParam));
+    }
+
+    /**
+     * 导出汇总统计报表
+     */
+    @Operation(summary = "导出汇总统计报表")
+    @SaCheckPermission("/biz/consumptionrecord/exportRecord")
+    @GetMapping(value = "/biz/consumptionrecord/exportRecord", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+    public void exportRecord(ConsumptionRecordPageParam consumptionRecordPageParam, HttpServletResponse response) throws IOException {
+        consumptionRecordService.exportRecord(consumptionRecordPageParam,response);
+    }
+
+    /**
+     * 预警记录
+     * @param consumptionRecordPageParam
+     * @return
+     */
+    @Operation(summary = "预警记录")
+    @SaCheckPermission("/biz/consumptionrecord/warnPage")
+    @GetMapping("/biz/consumptionrecord/warnPage")
+    public CommonResult<Page<BizMemberUserResult>> warnPage(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        return CommonResult.data(consumptionRecordService.warnPage(consumptionRecordPageParam));
+    }
 }

+ 13 - 2
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/entity/ConsumptionRecord.java

@@ -13,6 +13,9 @@
 package vip.xiaonuo.biz.modular.consumptionrecord.entity;
 
 import com.baomidou.mybatisplus.annotation.*;
+import com.fhs.core.trans.anno.Trans;
+import com.fhs.core.trans.constant.TransType;
+import com.fhs.core.trans.vo.TransPojo;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
@@ -30,7 +33,7 @@ import java.util.Date;
 @Getter
 @Setter
 @TableName("biz_consumption_record")
-public class ConsumptionRecord extends CommonEntity {
+public class ConsumptionRecord extends CommonEntity implements TransPojo {
 
     /** 主键ID */
     @TableId
@@ -66,7 +69,8 @@ public class ConsumptionRecord extends CommonEntity {
     private String consumptionRemark;
 
     /** 操作:1:赠送  2:扣减  3:消费 */
-    @Schema(description = "操作:1:赠送  2:扣减  3:消费")
+    @Schema(description = "操作:1:赠送  2:扣减  3:手动结算  4:扫码结算")
+    @Trans(type = TransType.DICTIONARY, key = "consumption_operate")
     private String consumptionOperate;
 
     /**总金额*/
@@ -99,4 +103,11 @@ public class ConsumptionRecord extends CommonEntity {
     /**现代金券余额*/
     @TableField(exist = false)
     private BigDecimal newVoucherBalance;
+
+    /**用户编码*/
+    @TableField(exist = false)
+    private String userCode;
+
+    /**调整类型*/
+    private String adjustType;
 }

+ 16 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/mapper/ConsumptionRecordMapper.java

@@ -17,7 +17,13 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
 import vip.xiaonuo.biz.modular.consumptionrecord.entity.ConsumptionRecord;
+import vip.xiaonuo.biz.modular.consumptionrecord.param.ConsumptionChart;
 import vip.xiaonuo.biz.modular.consumptionrecord.param.ConsumptionResult;
+import vip.xiaonuo.biz.modular.consumptionrecord.param.UserBalanceResult;
+import vip.xiaonuo.biz.modular.user.result.BizMemberUserResult;
+
+import java.util.List;
+import java.util.Map;
 
 /**
  * 消费记录Mapper接口
@@ -28,5 +34,15 @@ import vip.xiaonuo.biz.modular.consumptionrecord.param.ConsumptionResult;
 public interface ConsumptionRecordMapper extends BaseMapper<ConsumptionRecord> {
     Page<ConsumptionRecord> getPageList(@Param("page") Page<ConsumptionRecord> page, @Param("ew") QueryWrapper<ConsumptionRecord> ew);
 
+    Page<BizMemberUserResult> getWarnPageList(@Param("page") Page<ConsumptionRecord> page, @Param("ew") QueryWrapper<ConsumptionRecord> ew);
+
     ConsumptionResult getRecordTotal(@Param("ew") QueryWrapper<ConsumptionRecord> ew);
+
+    UserBalanceResult queryBalanceTotal(@Param("ew") QueryWrapper<ConsumptionRecord> ew);
+
+    List<Map<String,Object>> queryConsumptionChart(@Param("ew") QueryWrapper<ConsumptionRecord> ew);
+
+    List<Map<String,Object>> queryEachStore(@Param("orgIds") List<String> orgIds,@Param("startTime") String startTime, @Param("endTime") String endTime);
+
+
 }

+ 158 - 2
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/mapper/mapping/ConsumptionRecordMapper.xml

@@ -16,9 +16,11 @@
             IFNULL(cr.consumption_money,0) consumption_money,
             IFNULL(cr.voucher_balance,0) voucher_balance,
             IFNULL(cr.account_balance,0) account_balance,
+            cr.adjust_type,
             su.PHONE,
             su.ACCOUNT_BALANCE newAccountBalance,
-            su.VOUCHER_BALANCE newVoucherBalance
+            su.VOUCHER_BALANCE newVoucherBalance,
+            su.USER_REFERRAL_CODE userCode
         from biz_consumption_record cr
         left join SYS_USER su on cr.user_id = su.id
         left join SYS_ORG so on so.id = cr.consumption_org
@@ -30,10 +32,164 @@
     <select id="getRecordTotal" resultType="vip.xiaonuo.biz.modular.consumptionrecord.param.ConsumptionResult">
         select
             count(*) orderCount,
-            sum(bcr.consumption_money) orderMoney
+            ifnull(sum(bcr.consumption_money),0) orderMoney
         from biz_consumption_record bcr
         <where>
             ${ew.sqlSegment}
         </where>
     </select>
+
+    <select id="queryBalanceTotal"
+            resultType="vip.xiaonuo.biz.modular.consumptionrecord.param.UserBalanceResult">
+        select
+            count(*) userCount,
+            ifnull(sum(su.ACCOUNT_BALANCE),0) accountBalance,
+            ifnull(sum(su.VOUCHER_BALANCE),0) voucherBalance
+        from SYS_USER su
+        <where>
+            ${ew.sqlSegment}
+        </where>
+    </select>
+
+    <select id="queryConsumptionChart" resultType="java.util.Map">
+        SELECT
+            DATE_FORMAT( t1.dateTime, '%m-%d' ) dateTime,
+            IFNULL( t2.consumptionMoney, 0 ) AS consumptionMoney,
+            IFNULL( t2.consumptionCount, 0 ) consumptionCount,
+            IFNULL( t3.userCount, 0 ) userCount
+        FROM
+            (
+                SELECT
+                    CURDATE() AS dateTime UNION ALL
+                SELECT
+                    DATE_SUB( CURDATE(), INTERVAL 1 DAY ) AS dateTime UNION ALL
+                SELECT
+                    DATE_SUB( CURDATE(), INTERVAL 2 DAY ) AS dateTime UNION ALL
+                SELECT
+                    DATE_SUB( CURDATE(), INTERVAL 3 DAY ) AS dateTime UNION ALL
+                SELECT
+                    DATE_SUB( CURDATE(), INTERVAL 4 DAY ) AS dateTime UNION ALL
+                SELECT
+                    DATE_SUB( CURDATE(), INTERVAL 5 DAY ) AS dateTime UNION ALL
+                SELECT
+                    DATE_SUB( CURDATE(), INTERVAL 6 DAY ) AS dateTime
+            ) t1
+                LEFT JOIN (
+                SELECT LEFT
+                    ( bcr.consumption_time, 10 ) AS dayTime,
+                    IFNULL( SUM( bcr.consumption_money ), 0 ) consumptionMoney,
+                    count(*) consumptionCount
+                FROM
+                    biz_consumption_record bcr
+                WHERE
+                    bcr.delete_flag = 'NOT_DELETE'
+                and bcr.consumption_operate in('3','4')
+                GROUP BY
+                    LEFT ( bcr.consumption_time, 10 )
+                ORDER BY
+                    dayTime DESC
+            ) t2 ON t1.dateTime = t2.dayTime
+                LEFT JOIN (
+                SELECT LEFT
+                    ( bcr.create_time, 10 ) AS userDate,
+                    count(*) userCount
+                FROM
+                    SYS_USER bcr
+                WHERE
+                    bcr.delete_flag = 'NOT_DELETE'
+                GROUP BY
+                    LEFT ( bcr.create_time, 10 )
+                ORDER BY
+                    userDate DESC
+            ) t3 ON t1.dateTime = t3.userDate
+        ORDER BY
+            t1.dateTime
+    </select>
+
+    <select id="queryEachStore" resultType="java.util.Map">
+        SELECT
+            so.`name`,
+            so.CODE code,
+            ifnull(bcr.account_money,0) accountMoney,
+            ifnull(bcr.orderCount,0) orderCount,
+            ifnull(bcr.voucher_money,0) voucherMoney,
+            ifnull(su.userCount,0) userCount
+        FROM
+            SYS_ORG so
+            LEFT JOIN (
+                SELECT
+                    count(*) orderCount,
+                    IFNULL( sum( account_money ), 0 ) account_money,
+                    IFNULL( sum( voucher_money ), 0 ) voucher_money,
+                    bcr.consumption_org
+                FROM
+                    biz_consumption_record bcr
+                where bcr.DELETE_FLAG = 'NOT_DELETE'
+                and bcr.consumption_operate in ('3','4')
+                <if test="orgIds != null and orgIds.size > 0">
+                    AND bcr.consumption_org IN
+                    <foreach collection="orgIds" item="item" separator="," open="(" close=")">
+                        #{item}
+                    </foreach>
+                </if>
+                <if test="startTime !=null and endTime !=null and startTime!='' and endTime != '' ">
+                    and bcr.consumption_time between #{startTime} and #{endTime}
+                </if>
+                GROUP BY
+                    bcr.consumption_org
+            ) bcr ON bcr.consumption_org = so.id
+            LEFT JOIN ( SELECT count(*) userCount, su.ORG_ID FROM SYS_USER su
+                where su.DELETE_FLAG = 'NOT_DELETE'
+                <if test="orgIds != null and orgIds.size > 0">
+                    AND su.ORG_ID IN
+                    <foreach collection="orgIds" item="item" separator="," open="(" close=")">
+                        #{item}
+                    </foreach>
+                </if>
+                <if test="startTime !=null and endTime !=null and startTime!='' and endTime != '' ">
+                    and su.create_time between #{startTime} and #{endTime}
+                </if>
+                GROUP BY su.ORG_ID ) su ON su.ORG_ID = so.id
+            where so.PARENT_ID!=0
+            and so.DELETE_FLAG = 'NOT_DELETE'
+    </select>
+
+    <select id="getWarnPageList" resultType="vip.xiaonuo.biz.modular.user.result.BizMemberUserResult">
+        SELECT
+            a.userId id,
+            su.NAME name,
+            su.PHONE phone,
+            su.ACCOUNT account,
+            su.USER_STATUS,
+            a.count
+        FROM
+            (
+                SELECT
+                    a.REFERRAL_USER userId,
+                    ifnull( sum( a.count ), 0 ) count
+                FROM
+                    (
+                    SELECT
+                    count(*) count,
+                    bcr.user_id,
+                    su.REFERRAL_USER
+                    FROM
+                    biz_consumption_record bcr
+                    LEFT JOIN SYS_USER su ON bcr.user_id = su.id
+                    WHERE
+                    bcr.consumption_operate = '2'
+                    and bcr.DELETE_FLAG = 'NOT_DELETE'
+                    AND bcr.adjust_type = '2'
+                    GROUP BY
+                    bcr.user_id
+                    ) a
+                GROUP BY
+                    a.REFERRAL_USER
+            ) a
+                LEFT JOIN SYS_USER su ON su.id = a.userId
+        <where>
+            ${ew.sqlSegment}
+        </where>
+    </select>
+
 </mapper>

+ 20 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/ConsumptionChart.java

@@ -0,0 +1,20 @@
+package vip.xiaonuo.biz.modular.consumptionrecord.param;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class ConsumptionChart {
+    /**日期*/
+    private String dateTime;
+
+    /**销售额*/
+    private BigDecimal consumptionMoney;
+
+    /**订单数*/
+    private Integer consumptionCount;
+
+    /**注册数*/
+    private Integer userCount;
+}

+ 4 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/ConsumptionRecordAddParam.java

@@ -75,4 +75,8 @@ public class ConsumptionRecordAddParam {
     @Schema(description = "验证码")
     private String phoneCode;
 
+    /**调整类型*/
+    @Schema(description = "调整类型")
+    private String adjustType;
+
 }

+ 41 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/ConsumptionRecordExportResult.java

@@ -0,0 +1,41 @@
+package vip.xiaonuo.biz.modular.consumptionrecord.param;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.write.style.HeadStyle;
+import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class ConsumptionRecordExportResult {
+    /** 门店名称 */
+    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 120)
+    @ExcelProperty({"汇总统计", "门店名称"})
+    private String name;
+
+    /** 门店电话*/
+    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 120)
+    @ExcelProperty({"汇总统计", "门店编码"})
+    private String code;
+
+    /**订单数*/
+    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 120)
+    @ExcelProperty({"汇总统计", "消费","订单数"})
+    private Integer orderCount;
+
+    /**金额*/
+    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 120)
+    @ExcelProperty({"汇总统计", "消费","账户消费金额"})
+    private BigDecimal accountMoney;
+
+    /**金额*/
+    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 120)
+    @ExcelProperty({"汇总统计", "消费","代金券消费金额"})
+    private BigDecimal voucherMoney;
+
+    /**注册会员数*/
+    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 120)
+    @ExcelProperty({"汇总统计", "注册会员数"})
+    private Integer userCount;
+}

+ 8 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/ConsumptionRecordPageParam.java

@@ -61,4 +61,12 @@ public class ConsumptionRecordPageParam {
     private String consumptionTimeBegin;
     private String consumptionTimeEnd;
 
+    /**调整类别*/
+    private String adjustType;
+
+    /**推荐人id*/
+    private String recordId;
+
+    private String consumptionTime;
+
 }

+ 17 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/param/UserBalanceResult.java

@@ -0,0 +1,17 @@
+package vip.xiaonuo.biz.modular.consumptionrecord.param;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class UserBalanceResult {
+    /**账户余额*/
+    private BigDecimal accountBalance;
+
+    /**代金券余额*/
+    private BigDecimal voucherBalance;
+
+    /**会员数*/
+    private Integer userCount;
+}

+ 20 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/service/ConsumptionRecordService.java

@@ -12,12 +12,17 @@
  */
 package vip.xiaonuo.biz.modular.consumptionrecord.service;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import jakarta.servlet.http.HttpServletResponse;
 import vip.xiaonuo.biz.modular.consumptionrecord.entity.ConsumptionRecord;
 import vip.xiaonuo.biz.modular.consumptionrecord.param.*;
+import vip.xiaonuo.biz.modular.user.result.BizMemberUserResult;
 
+import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 消费记录Service接口
@@ -76,4 +81,19 @@ public interface ConsumptionRecordService extends IService<ConsumptionRecord> {
     ConsumptionRecord queryEntity(String id);
 
     ConsumptionResult getRecordTotal();
+
+    Map<String,Object> queryTodayRevenue(ConsumptionRecordPageParam consumptionRecordPageParam);
+
+    ConsumptionResult getRecordTotal(ConsumptionRecordPageParam consumptionRecordPageParam);
+
+    UserBalanceResult queryBalanceTotal(ConsumptionRecordPageParam consumptionRecordPageParam);
+
+    Map<String,Object> queryConsumptionChart(ConsumptionRecordPageParam consumptionRecordPageParam);
+
+    Map<String,Object> queryEachStore(ConsumptionRecordPageParam consumptionRecordPageParam);
+
+    Page<BizMemberUserResult> warnPage(ConsumptionRecordPageParam consumptionRecordPageParam);
+
+    void exportRecord(ConsumptionRecordPageParam consumptionRecordPageParam, HttpServletResponse response) throws IOException;
+
 }

+ 261 - 15
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/consumptionrecord/service/impl/ConsumptionRecordServiceImpl.java

@@ -14,6 +14,7 @@ package vip.xiaonuo.biz.modular.consumptionrecord.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.PhoneUtil;
@@ -21,7 +22,11 @@ import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Maps;
 import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.compress.utils.Lists;
 import org.apache.commons.lang3.StringUtils;
 import org.checkerframework.checker.units.qual.C;
 import org.springframework.stereotype.Service;
@@ -29,9 +34,11 @@ import org.springframework.transaction.annotation.Transactional;
 import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
 import vip.xiaonuo.biz.modular.consumptionrecord.param.*;
 import vip.xiaonuo.biz.modular.user.entity.BizUser;
+import vip.xiaonuo.biz.modular.user.result.BizMemberUserResult;
 import vip.xiaonuo.biz.modular.user.service.BizUserService;
 import vip.xiaonuo.biz.modular.userfundchangerecord.entity.BizUserFundChangeRecord;
 import vip.xiaonuo.biz.modular.userfundchangerecord.service.BizUserFundChangeRecordService;
+import vip.xiaonuo.biz.modular.utils.CommonExportUtil;
 import vip.xiaonuo.common.cache.CommonCacheOperator;
 import vip.xiaonuo.common.enums.CommonSortOrderEnum;
 import vip.xiaonuo.common.exception.CommonException;
@@ -40,10 +47,12 @@ import vip.xiaonuo.biz.modular.consumptionrecord.entity.ConsumptionRecord;
 import vip.xiaonuo.biz.modular.consumptionrecord.mapper.ConsumptionRecordMapper;
 import vip.xiaonuo.biz.modular.consumptionrecord.service.ConsumptionRecordService;
 import vip.xiaonuo.common.util.CommonCryptogramUtil;
+import vip.xiaonuo.dev.api.DevConfigApi;
 
+import java.io.IOException;
 import java.math.BigDecimal;
-import java.util.Date;
-import java.util.List;
+import java.math.RoundingMode;
+import java.util.*;
 
 /**
  * 消费记录Service接口实现类
@@ -63,6 +72,9 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
     @Resource
     private BizUserFundChangeRecordService bizUserFundChangeRecordService;
 
+    @Resource
+    private DevConfigApi devConfigApi;
+
     @Override
     public Page<ConsumptionRecord> page(ConsumptionRecordPageParam consumptionRecordPageParam) {
         QueryWrapper<ConsumptionRecord> queryWrapper = new QueryWrapper<ConsumptionRecord>().checkSqlInjection();
@@ -70,7 +82,8 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
             queryWrapper.like("su.name",consumptionRecordPageParam.getUserName());
         }
         if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionOperate())){
-            queryWrapper.eq("cr.consumption_operate",consumptionRecordPageParam.getConsumptionOperate());
+            List<String> list = Arrays.asList(consumptionRecordPageParam.getConsumptionOperate().split(","));
+            queryWrapper.in("cr.consumption_operate",list);
         }
         if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getOrgId())){
             queryWrapper.eq("cr.consumption_org",consumptionRecordPageParam.getOrgId());
@@ -79,6 +92,16 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
                 ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeEnd())){
             queryWrapper.between("cr.consumption_time",consumptionRecordPageParam.getConsumptionTimeBegin()+" 00:00:00",consumptionRecordPageParam.getConsumptionTimeEnd()+" 23:59:59");
         }
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getAdjustType())){
+            queryWrapper.eq("cr.adjust_type",consumptionRecordPageParam.getAdjustType());
+        }
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getRecordId())){
+            List<String> idList = Lists.newArrayList();
+            bizUserService.list(new QueryWrapper<BizUser>().lambda().
+                    eq(BizUser::getReferralUser, consumptionRecordPageParam.getRecordId())).
+                    forEach(user->idList.add(user.getId()));
+            queryWrapper.in("cr.user_id",idList);
+        }
         // 校验数据范围
         List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
         if (ObjectUtil.isNotEmpty(loginUserDataScope)) {
@@ -86,6 +109,7 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
         } else {
             queryWrapper.in("cr.user_id", StpLoginUserUtil.getLoginUser().getId());
         }
+        queryWrapper.eq("cr.DELETE_FLAG","NOT_DELETE");
         queryWrapper.orderByDesc("cr.create_time");
         Page<ConsumptionRecord> pageList = this.getBaseMapper().getPageList(CommonPageRequest.defaultPage(), queryWrapper);
         for(ConsumptionRecord consumptionRecord : pageList.getRecords()){
@@ -95,13 +119,13 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
             if(StringUtils.equals(consumptionRecord.getConsumptionOperate(),"3") || StringUtils.equals(consumptionRecord.getConsumptionOperate(),"4")){
                 //消费结算,设置下说明
                 if(StringUtils.equals(consumptionRecord.getConsumptionType(),"1")){
-                    consumptionRecord.setConsumptionRemark("代金券消费"+consumptionRecord.getVoucherMoney().stripTrailingZeros().toPlainString()+"元");
+                    consumptionRecord.setConsumptionRemark("积分消费"+consumptionRecord.getVoucherMoney().stripTrailingZeros().toPlainString()+"个");
                 }
                 if(StringUtils.equals(consumptionRecord.getConsumptionType(),"2")){
-                    consumptionRecord.setConsumptionRemark("账户余额消费"+consumptionRecord.getAccountMoney().stripTrailingZeros().toPlainString()+"元");
+                    consumptionRecord.setConsumptionRemark("糕点消费"+consumptionRecord.getAccountMoney().stripTrailingZeros().toPlainString()+"个");
                 }
                 if(StringUtils.equals(consumptionRecord.getConsumptionType(),"3")){
-                    consumptionRecord.setConsumptionRemark("代金券消费"+consumptionRecord.getVoucherMoney().stripTrailingZeros().toPlainString()+"元"+",账户余额消费"+consumptionRecord.getAccountMoney().stripTrailingZeros().toPlainString()+"元");
+                    consumptionRecord.setConsumptionRemark("积分消费"+consumptionRecord.getVoucherMoney().stripTrailingZeros().toPlainString()+"个"+",糕点消费"+consumptionRecord.getAccountMoney().stripTrailingZeros().toPlainString()+"个");
                 }
             }
         }
@@ -138,24 +162,18 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
             }
         }else if(StringUtils.equals(consumptionRecordAddParam.getConsumptionOperate(),"2")){
             bizUserFundChangeRecord.setChangeType("5");
-            if(bizUser.getVoucherBalance().compareTo(BigDecimal.ZERO) == 0){
-                throw new CommonException("会员当前代金券余额为0,不可扣减!");
-            }
-            if(bizUser.getAccountBalance().compareTo(BigDecimal.ZERO) == 0){
-                throw new CommonException("会员当前账户余额为0,不可扣减!");
-            }
             //会员扣减
             if(StringUtils.equals(consumptionRecordAddParam.getConsumptionType(),"1")){
                 //选择代金券,用户原有代金券金额-本次扣减金额
                 if(consumptionRecord.getConsumptionMoney().compareTo(bizUser.getVoucherBalance()) > 0){
-                    throw new CommonException("会员当前代金券余额不足,不可扣减!");
+                    throw new CommonException("会员当前账户积分不足,不可扣减!");
                 }
                 bizUser.setVoucherBalance(bizUser.getVoucherBalance().subtract(consumptionRecordAddParam.getConsumptionMoney()));
                 consumptionRecord.setVoucherMoney(consumptionRecordAddParam.getConsumptionMoney());
             }else if(StringUtils.equals(consumptionRecordAddParam.getConsumptionType(),"2")){
                 //选择账户余额,用户原有余额-本次扣减余额
                 if(consumptionRecord.getConsumptionMoney().compareTo(bizUser.getAccountBalance()) > 0){
-                    throw new CommonException("会员当前账户余额不足,不可扣减!");
+                    throw new CommonException("会员当前账户糕点不足,不可扣减!");
                 }
                 bizUser.setAccountBalance(bizUser.getAccountBalance().subtract(consumptionRecordAddParam.getConsumptionMoney()));
                 consumptionRecord.setAccountMoney(consumptionRecordAddParam.getConsumptionMoney());
@@ -184,7 +202,7 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
                 BigDecimal subtract = consumptionRecordAddParam.getConsumptionMoney().subtract(bizUser.getVoucherBalance());
                 //如果差额大于账户余额,表示账户余额不足
                 if(subtract.compareTo(bizUser.getAccountBalance()) > 0){
-                    throw new CommonException("账户余额不足,请先充值!");
+                    throw new CommonException("账户糕点不足,请先充值!");
                 }
                 //记录消费信息
                 consumptionRecord.setVoucherMoney(bizUser.getVoucherBalance());
@@ -251,6 +269,15 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
                 throw new CommonException("手机号码:{}格式错误", consumptionRecordAddParam.getPhoneNumber());
             }
         }
+        if(ObjectUtil.isNotEmpty(consumptionRecordAddParam.getUserId())){
+            BizUser bizUser = bizUserService.getById(consumptionRecordAddParam.getUserId());
+            if(ObjectUtil.isEmpty(bizUser)){
+                throw new CommonException("用户不存在!");
+            }
+            if(StringUtils.equals(bizUser.getUserStatus(),"DISABLED")){
+                throw new CommonException("用户账号冻结,不可消费!");
+            }
+        }
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -296,8 +323,227 @@ public class ConsumptionRecordServiceImpl extends ServiceImpl<ConsumptionRecordM
             consumptionResult.setOrderMoney(new BigDecimal(0));
             return consumptionResult;
         }
+        queryWrapper.eq("bcr.DELETE_FLAG","NOT_DELETE");
         queryWrapper.in("bcr.consumption_operate",'3','4');
         queryWrapper.between("bcr.consumption_time", format+" 00:00:00",format+" 23:59:59");
         return this.getBaseMapper().getRecordTotal(queryWrapper);
     }
+
+    @Override
+    public Map<String, Object> queryTodayRevenue(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        if(ObjectUtil.isEmpty(consumptionRecordPageParam.getConsumptionTime())){
+            throw new CommonException("时间不能为空!");
+        }
+        Map<String,Object> map = Maps.newHashMap();
+        QueryWrapper<ConsumptionRecord> todayQuery = getQueryWrapper(consumptionRecordPageParam);
+        ConsumptionResult todayTotal = this.getBaseMapper().getRecordTotal(todayQuery);
+        map.put("orderTotal",todayTotal.getOrderCount());
+        map.put("accountTotal",todayTotal.getOrderMoney());
+        String format = DateUtil.format(DateUtil.offsetDay(DateUtil.parseDate(consumptionRecordPageParam.getConsumptionTime()), -1), "yyyy-MM-dd");
+        consumptionRecordPageParam.setConsumptionTime(format);
+        QueryWrapper<ConsumptionRecord> yesQuery = getQueryWrapper(consumptionRecordPageParam);
+        ConsumptionResult yesTotal = this.getBaseMapper().getRecordTotal(yesQuery);
+        if(todayTotal.getOrderCount().compareTo(yesTotal.getOrderCount()) >= 0){
+            map.put("orderFlag","rise");
+            map.put("orderContrast",todayTotal.getOrderCount()-yesTotal.getOrderCount());
+            if(todayTotal.getOrderCount()!=0 && yesTotal.getOrderCount()!=0){
+                map.put("orderPercent",((todayTotal.getOrderCount()-yesTotal.getOrderCount())/yesTotal.getOrderCount())*100);
+            }else{
+                map.put("orderPercent",0);
+            }
+        }else{
+            map.put("orderFlag","decline");
+            map.put("orderContrast",yesTotal.getOrderCount()-todayTotal.getOrderCount());
+            if(todayTotal.getOrderCount()!=0 && yesTotal.getOrderCount()!=0){
+                map.put("orderPercent",((yesTotal.getOrderCount()-todayTotal.getOrderCount())/yesTotal.getOrderCount())*100);
+            }else{
+                map.put("orderPercent",0);
+            }
+        }
+
+        BigDecimal todayMoney = todayTotal.getOrderMoney();
+        BigDecimal yesMoney = yesTotal.getOrderMoney();
+        if(todayTotal.getOrderMoney().compareTo(yesTotal.getOrderMoney()) > 0){
+            map.put("accountFlag","rise");
+            map.put("accountContrast",todayMoney.subtract(yesMoney));
+
+            if(todayMoney.compareTo(BigDecimal.ZERO)!=0 && yesMoney.compareTo(BigDecimal.ZERO) !=0){
+                map.put("accountPercent",(todayMoney.subtract(yesMoney)).divide(yesMoney).multiply(new BigDecimal(100)).setScale(1, RoundingMode.HALF_UP));
+            }else{
+                map.put("accountPercent",0);
+            }
+        }else{
+            map.put("accountFlag","decline");
+            map.put("accountContrast",yesMoney.subtract(todayMoney));
+
+            if(todayMoney.compareTo(BigDecimal.ZERO)!=0 && yesMoney.compareTo(BigDecimal.ZERO) !=0){
+                map.put("accountPercent",(yesMoney.subtract(todayMoney)).divide(yesMoney).multiply(new BigDecimal(100)).setScale(1,RoundingMode.HALF_UP));
+            }else{
+                map.put("accountPercent",0);
+            }
+        }
+        return map;
+    }
+
+    public QueryWrapper<ConsumptionRecord> getQueryWrapper(ConsumptionRecordPageParam consumptionRecordPageParam){
+        QueryWrapper<ConsumptionRecord> queryWrapper = new QueryWrapper<>();
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTime())){
+            queryWrapper.between("bcr.consumption_time",consumptionRecordPageParam.getConsumptionTime()+" 00:00:00",
+                    consumptionRecordPageParam.getConsumptionTime()+" 23:59:59");
+        }
+        // 校验数据范围
+        List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
+        if (ObjectUtil.isNotEmpty(loginUserDataScope)) {
+            queryWrapper.in("bcr.consumption_org", loginUserDataScope);
+        }else{
+            return null;
+        }
+        queryWrapper.eq("bcr.DELETE_FLAG","NOT_DELETE");
+        queryWrapper.in("bcr.consumption_operate",'3','4');
+        return queryWrapper;
+    }
+
+    @Override
+    public ConsumptionResult getRecordTotal(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        QueryWrapper<ConsumptionRecord> queryWrapper = new QueryWrapper<>();
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getOrgId())){
+            queryWrapper.eq("bcr.consumption_org",consumptionRecordPageParam.getOrgId());
+        }
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeBegin()) && ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeEnd())){
+            queryWrapper.between("bcr.consumption_time",consumptionRecordPageParam.getConsumptionTimeBegin()+" 00:00:00",consumptionRecordPageParam.getConsumptionTimeEnd()+" 23:59:59");
+        }
+        queryWrapper.eq("bcr.DELETE_FLAG","NOT_DELETE");
+        queryWrapper.in("bcr.consumption_operate","3","4");
+        return this.getBaseMapper().getRecordTotal(queryWrapper);
+    }
+
+
+
+    @Override
+    public UserBalanceResult queryBalanceTotal(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        QueryWrapper<ConsumptionRecord> queryWrapper = getAccountQueryWrapper(consumptionRecordPageParam);
+        UserBalanceResult userBalanceResult = this.getBaseMapper().queryBalanceTotal(queryWrapper);
+        QueryWrapper<ConsumptionRecord> customQueryWrapper = getCustomQueryWrapper(consumptionRecordPageParam);
+        UserBalanceResult result = this.getBaseMapper().queryBalanceTotal(customQueryWrapper);
+        userBalanceResult.setUserCount(result.getUserCount());
+        return userBalanceResult;
+    }
+
+    public QueryWrapper<ConsumptionRecord> getAccountQueryWrapper(ConsumptionRecordPageParam consumptionRecordPageParam){
+        QueryWrapper<ConsumptionRecord> queryWrapper = new QueryWrapper<>();
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getOrgId())){
+            queryWrapper.eq("su.ORG_ID",consumptionRecordPageParam.getOrgId());
+        }
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeBegin()) && ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeEnd())){
+            queryWrapper.between("su.CREATE_TIME",consumptionRecordPageParam.getConsumptionTimeBegin()+" 00:00:00",consumptionRecordPageParam.getConsumptionTimeEnd()+" 23:59:59");
+        }
+        queryWrapper.in("su.USER_TYPE","2","3");
+        queryWrapper.eq("su.DELETE_FLAG","NOT_DELETE");
+        return queryWrapper;
+    }
+
+    public QueryWrapper<ConsumptionRecord> getCustomQueryWrapper(ConsumptionRecordPageParam consumptionRecordPageParam){
+        QueryWrapper<ConsumptionRecord> queryWrapper = new QueryWrapper<>();
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeBegin()) && ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeEnd())){
+            queryWrapper.between("su.CREATE_TIME",consumptionRecordPageParam.getConsumptionTimeBegin()+" 00:00:00",consumptionRecordPageParam.getConsumptionTimeEnd()+" 23:59:59");
+        }
+        queryWrapper.in("su.USER_TYPE","2","3");
+        queryWrapper.eq("su.DELETE_FLAG","NOT_DELETE");
+        return queryWrapper;
+    }
+
+
+
+    @Override
+    public Map<String,Object> queryConsumptionChart(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        List<String> timeList = Lists.newArrayList();
+        List<BigDecimal> moneyList = Lists.newArrayList();
+        List<Integer> orderList = Lists.newArrayList();
+        List<Integer> userCountList = Lists.newArrayList();
+        Map<String,Object> result = Maps.newHashMap();
+        QueryWrapper<ConsumptionRecord> queryWrapper = new QueryWrapper<>();
+        List<Map<String, Object>> mapList = this.getBaseMapper().queryConsumptionChart(queryWrapper);
+        for(Map<String,Object> map: mapList){
+            timeList.add(MapUtils.getString(map,"dateTime"));
+            moneyList.add(new BigDecimal(MapUtils.getString(map,"consumptionMoney")));
+            orderList.add(MapUtils.getInteger(map,"consumptionCount"));
+            userCountList.add(MapUtils.getInteger(map,"userCount"));
+        }
+        result.put("dateTime",timeList);
+        result.put("consumptionMoney",moneyList);
+        result.put("consumptionCount",orderList);
+        result.put("userCount",userCountList);
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> queryEachStore(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        Map<String,Object> result = Maps.newHashMap();
+        List<Map<String, Object>> mapList = getList(consumptionRecordPageParam);
+        result.put("dataList",mapList);
+        return result;
+    }
+
+    @Override
+    public Page<BizMemberUserResult> warnPage(ConsumptionRecordPageParam consumptionRecordPageParam) {
+        Page<BizMemberUserResult> warnPageList = new Page();
+        String warnSwitch = devConfigApi.getValueByKey("warnSwitch");
+        if(ObjectUtil.isNotEmpty(warnSwitch) && StringUtils.equals(warnSwitch,"true")){
+            //预警开关开启
+            long warnCount = Long.parseLong(devConfigApi.getValueByKey("warnCount"));
+            QueryWrapper queryWrapper = new QueryWrapper<ConsumptionRecord>();
+            queryWrapper.gt("a.count",warnCount);
+            if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getUserName())){
+                queryWrapper.like("su.NAME",consumptionRecordPageParam.getUserName());
+            }
+            warnPageList = this.getBaseMapper().getWarnPageList(CommonPageRequest.defaultPage(), queryWrapper);
+            for(BizMemberUserResult bizMemberUserResult : warnPageList.getRecords()){
+                if(ObjectUtil.isNotEmpty(bizMemberUserResult.getPhone())){
+                    bizMemberUserResult.setPhone(CommonCryptogramUtil.doSm4CbcDecrypt(bizMemberUserResult.getPhone()));
+                }
+            }
+        }
+        return warnPageList;
+    }
+
+
+    public List<Map<String, Object>> getList(ConsumptionRecordPageParam consumptionRecordPageParam){
+        List<String> orgIds = new ArrayList<>();
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getOrgId())){
+            orgIds = Arrays.asList(consumptionRecordPageParam.getOrgId().split(","));
+        }
+        String startTime = null;
+        String endTime = null;
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeBegin())){
+            startTime=consumptionRecordPageParam.getConsumptionTimeBegin()+" 00:00:00";
+        }
+        if(ObjectUtil.isNotEmpty(consumptionRecordPageParam.getConsumptionTimeEnd())){
+            endTime = consumptionRecordPageParam.getConsumptionTimeEnd()+" 23:59:59";
+        }
+        List<Map<String, Object>> mapList = this.getBaseMapper().queryEachStore(orgIds, startTime, endTime);
+        return mapList;
+    }
+
+    @Override
+    public void exportRecord(ConsumptionRecordPageParam consumptionRecordPageParam, HttpServletResponse response) throws IOException {
+        List<Map<String, Object>> mapList = getList(consumptionRecordPageParam);
+        String fileName = "汇总统计报表.xlsx";
+        List<ConsumptionRecordExportResult> list = Lists.newArrayList();
+        for(Map<String,Object> map:mapList){
+            ConsumptionRecordExportResult consumeRecordExportResult = new ConsumptionRecordExportResult();
+            consumeRecordExportResult.setName(MapUtils.getString(map,"name"));
+            consumeRecordExportResult.setCode(MapUtils.getString(map,"code"));
+            consumeRecordExportResult.setOrderCount(MapUtils.getInteger(map,"orderCount"));
+            consumeRecordExportResult.setAccountMoney(new BigDecimal(MapUtils.getString(map,"accountMoney")));
+            consumeRecordExportResult.setVoucherMoney(new BigDecimal(MapUtils.getString(map,"voucherMoney")));
+            consumeRecordExportResult.setUserCount(MapUtils.getInteger(map,"userCount"));
+            list.add(consumeRecordExportResult);
+        }
+        CommonExportUtil.export(fileName, ConsumptionRecordExportResult.class,list,response,"汇总统计报表");
+    }
+
+
+    public static void main(String[] args) {
+        System.out.printf("加密后手机号:"+CommonCryptogramUtil.doSm4CbcEncrypt("15240260262"));
+    }
 }

+ 4 - 1
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/entity/BizCouponRecord.java

@@ -85,7 +85,7 @@ public class BizCouponRecord extends CommonEntity {
     private String destroyOrgName;
     /**优惠券金额*/
     @TableField(exist = false)
-    private String couponAmount;
+    private BigDecimal couponAmount;
     /**会员姓名*/
     @TableField(exist = false)
     private String userName;
@@ -93,4 +93,7 @@ public class BizCouponRecord extends CommonEntity {
     @TableField(exist = false)
     private String phone;
 
+    /**清除状态  1:已清除  0:未清除*/
+    private Integer destroyStatus;
+
 }

+ 6 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/mapper/BizCouponRecordMapper.java

@@ -18,6 +18,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
 import vip.xiaonuo.biz.modular.couponrecord.entity.BizCouponRecord;
 
+import java.util.List;
+
 /**
  * 优惠券记录Mapper接口
  *
@@ -26,4 +28,8 @@ import vip.xiaonuo.biz.modular.couponrecord.entity.BizCouponRecord;
  **/
 public interface BizCouponRecordMapper extends BaseMapper<BizCouponRecord> {
     Page<BizCouponRecord> getPageList(@Param("page") Page<BizCouponRecord> page, @Param("ew") QueryWrapper<BizCouponRecord> ew);
+
+    BizCouponRecord getCouponNoList(@Param("ew") QueryWrapper<BizCouponRecord> ew);
+
+    List<BizCouponRecord> getList(@Param("ew") QueryWrapper<BizCouponRecord> ew);
 }

+ 32 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/mapper/mapping/BizCouponRecordMapper.xml

@@ -25,4 +25,36 @@
         left join SYS_ORG so on so.id = bcr.org_id
         ${ew.customSqlSegment}
     </select>
+    <select id="getCouponNoList" resultType="vip.xiaonuo.biz.modular.couponrecord.entity.BizCouponRecord">
+        SELECT
+            brr.user_id,
+            GROUP_CONCAT(bcr.coupon_no ORDER BY brr.user_id ASC SEPARATOR ', ') AS coupon_no
+        FROM
+            biz_coupon_record bcr
+            LEFT JOIN biz_recharge_record brr ON bcr.recharge_record_id = brr.id
+            ${ew.customSqlSegment}
+    </select>
+    <select id="getList" resultType="vip.xiaonuo.biz.modular.couponrecord.entity.BizCouponRecord">
+        select
+            bcr.id,
+            bcr.coupon_no,
+            bcr.time,
+            bcr.coupon_status,
+            bcr.start_time,
+            bcr.end_time,
+            bcr.destroy_user,
+            bcr.destroy_time,
+            bcr.org_id,
+            su.name userName,
+            su.phone,
+            u.name destroyUserName,
+            so.name destroyOrgName,
+            brr.coupon_amount
+        from biz_coupon_record bcr
+                 left join biz_recharge_record brr on bcr.recharge_record_id = brr.id
+                 left join SYS_USER su on su.id = brr.user_id
+                 left join SYS_USER u on u.id = bcr.destroy_user
+                 left join SYS_ORG so on so.id = bcr.org_id
+            ${ew.customSqlSegment}
+    </select>
 </mapper>

+ 7 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/param/BizCouponRecordPageParam.java

@@ -56,4 +56,11 @@ public class BizCouponRecordPageParam {
     @Schema(description = "是否核销: 0.否 1.是")
     private Boolean couponStatus;
 
+    private String beginTime;
+    private String endTime;
+
+    private String orgId;
+
+    private String userId;
+
 }

+ 4 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/service/BizCouponRecordService.java

@@ -85,6 +85,10 @@ public interface BizCouponRecordService extends IService<BizCouponRecord> {
      */
     BizCouponRecord queryByCode(BizCouponRecordPageParam bizCouponRecordPageParam);
 
+    BizCouponRecord getCouponNoList(BizCouponRecordPageParam bizCouponRecordPageParam);
+
+    List<BizCouponRecord> getList(BizCouponRecordPageParam bizCouponRecordPageParam);
+
     /**优惠券核销*/
     void destroy(BizCouponRecordEditParam bizCouponRecordEditParam);
 }

+ 35 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/couponrecord/service/impl/BizCouponRecordServiceImpl.java

@@ -19,10 +19,13 @@ import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.annotation.Resource;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.biz.modular.rechargerecord.entity.BizRechargeRecord;
+import vip.xiaonuo.biz.modular.rechargerecord.service.BizRechargeRecordService;
 import vip.xiaonuo.common.enums.CommonSortOrderEnum;
 import vip.xiaonuo.common.exception.CommonException;
 import vip.xiaonuo.common.page.CommonPageRequest;
@@ -47,6 +50,9 @@ import java.util.List;
 @Service
 public class BizCouponRecordServiceImpl extends ServiceImpl<BizCouponRecordMapper, BizCouponRecord> implements BizCouponRecordService {
 
+    @Resource
+    private BizRechargeRecordService bizRechargeRecordService;
+
     @Override
     public Page<BizCouponRecord> page(BizCouponRecordPageParam bizCouponRecordPageParam) {
         QueryWrapper<BizCouponRecord> queryWrapper = new QueryWrapper<BizCouponRecord>().checkSqlInjection();
@@ -56,6 +62,12 @@ public class BizCouponRecordServiceImpl extends ServiceImpl<BizCouponRecordMappe
         if(ObjectUtil.isNotEmpty(bizCouponRecordPageParam.getCouponStatus())) {
             queryWrapper.eq("bcr.coupon_status", bizCouponRecordPageParam.getCouponStatus());
         }
+        if(ObjectUtil.isNotEmpty(bizCouponRecordPageParam.getBeginTime()) && ObjectUtil.isNotEmpty(bizCouponRecordPageParam.getEndTime())){
+            queryWrapper.between("bcr.time",bizCouponRecordPageParam.getBeginTime()+" 00:00:00",bizCouponRecordPageParam.getEndTime()+" 23:59:59");
+        }
+        if(ObjectUtil.isNotEmpty(bizCouponRecordPageParam.getOrgId())){
+            queryWrapper.eq("bcr.org_id",bizCouponRecordPageParam.getOrgId());
+        }
         // 校验数据范围
         List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
         if (ObjectUtil.isEmpty(loginUserDataScope)) {
@@ -114,9 +126,32 @@ public class BizCouponRecordServiceImpl extends ServiceImpl<BizCouponRecordMappe
                 eq(BizCouponRecord::getCouponNo, bizCouponRecordPageParam.getCouponNo()).
                 eq(BizCouponRecord::getCouponStatus,"0").
                 last("limit 1"));
+        BizRechargeRecord rechargeRecord = bizRechargeRecordService.getById(bizCouponRecord.getRechargeRecordId());
+        if(ObjectUtil.isNotEmpty(rechargeRecord)){
+            bizCouponRecord.setCouponAmount(rechargeRecord.getCouponAmount());
+        }
         return bizCouponRecord;
     }
 
+    @Override
+    public BizCouponRecord getCouponNoList(BizCouponRecordPageParam bizCouponRecordPageParam) {
+        QueryWrapper<BizCouponRecord> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("brr.user_id",bizCouponRecordPageParam.getUserId());
+        queryWrapper.groupBy("brr.user_id");
+        BizCouponRecord couponNoList = this.getBaseMapper().getCouponNoList(queryWrapper);
+        return couponNoList;
+    }
+
+    @Override
+    public List<BizCouponRecord> getList(BizCouponRecordPageParam bizCouponRecordPageParam) {
+        QueryWrapper<BizCouponRecord> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("brr.user_id",bizCouponRecordPageParam.getUserId());
+        queryWrapper.eq("bcr.coupon_status","0");
+        queryWrapper.eq("bcr.delete_flag","NOT_DELETE");
+        List<BizCouponRecord> list = this.getBaseMapper().getList(queryWrapper);
+        return list;
+    }
+
     /***/
     @Override
     public void destroy(BizCouponRecordEditParam bizCouponRecordEditParam) {

+ 123 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/controller/BizDisableRecordController.java

@@ -0,0 +1,123 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.biz.modular.disablerecord.entity.BizDisableRecord;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordAddParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordEditParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordIdParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordPageParam;
+import vip.xiaonuo.biz.modular.disablerecord.service.BizDisableRecordService;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 清除记录控制器
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ */
+@Tag(name = "清除记录控制器")
+@RestController
+@Validated
+public class BizDisableRecordController {
+
+    @Resource
+    private BizDisableRecordService bizDisableRecordService;
+
+    /**
+     * 获取清除记录分页
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    @Operation(summary = "获取清除记录分页")
+    @SaCheckPermission("/biz/disablerecord/page")
+    @GetMapping("/biz/disablerecord/page")
+    public CommonResult<Page<BizDisableRecord>> page(BizDisableRecordPageParam bizDisableRecordPageParam) {
+        return CommonResult.data(bizDisableRecordService.page(bizDisableRecordPageParam));
+    }
+
+    /**
+     * 添加清除记录
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    @Operation(summary = "添加清除记录")
+    @CommonLog("添加清除记录")
+    @SaCheckPermission("/biz/disablerecord/add")
+    @PostMapping("/biz/disablerecord/add")
+    public CommonResult<String> add(@RequestBody @Valid BizDisableRecordAddParam bizDisableRecordAddParam) {
+        bizDisableRecordService.add(bizDisableRecordAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑清除记录
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    @Operation(summary = "编辑清除记录")
+    @CommonLog("编辑清除记录")
+    @SaCheckPermission("/biz/disablerecord/edit")
+    @PostMapping("/biz/disablerecord/edit")
+    public CommonResult<String> edit(@RequestBody @Valid BizDisableRecordEditParam bizDisableRecordEditParam) {
+        bizDisableRecordService.edit(bizDisableRecordEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除清除记录
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    @Operation(summary = "删除清除记录")
+    @CommonLog("删除清除记录")
+    @SaCheckPermission("/biz/disablerecord/delete")
+    @PostMapping("/biz/disablerecord/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   List<BizDisableRecordIdParam> bizDisableRecordIdParamList) {
+        bizDisableRecordService.delete(bizDisableRecordIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取清除记录详情
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    @Operation(summary = "获取清除记录详情")
+    @SaCheckPermission("/biz/disablerecord/detail")
+    @GetMapping("/biz/disablerecord/detail")
+    public CommonResult<BizDisableRecord> detail(@Valid BizDisableRecordIdParam bizDisableRecordIdParam) {
+        return CommonResult.data(bizDisableRecordService.detail(bizDisableRecordIdParam));
+    }
+}

+ 64 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/entity/BizDisableRecord.java

@@ -0,0 +1,64 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import vip.xiaonuo.common.pojo.CommonEntity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 清除记录实体
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+@Getter
+@Setter
+@TableName("biz_disable_record")
+public class BizDisableRecord extends CommonEntity {
+
+    /** 主键ID */
+    @TableId
+    @Schema(description = "主键ID")
+    private String id;
+
+    /** 用户ID */
+    @Schema(description = "用户ID")
+    private String userId;
+
+    /** 账户余额清除金额 */
+    @Schema(description = "账户余额清除金额")
+    private BigDecimal accountBalance;
+
+    /** 代金券清除金额 */
+    @Schema(description = "代金券清除金额")
+    private BigDecimal voucherBalance;
+
+    /** 清除蛋糕券编码 */
+    @Schema(description = "清除蛋糕券编码")
+    private String couponCode;
+
+    /**用户名*/
+    @TableField(exist = false)
+    private String userName;
+
+    /**手机号*/
+    @TableField(exist = false)
+    private String phone;
+
+}

+ 34 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/enums/BizDisableRecordEnum.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.enums;
+
+import lombok.Getter;
+
+/**
+ * 清除记录枚举
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+@Getter
+public enum BizDisableRecordEnum {
+
+    /** 测试 */
+    TEST("TEST");
+
+    private final String value;
+
+    BizDisableRecordEnum(String value) {
+        this.value = value;
+    }
+}

+ 30 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/mapper/BizDisableRecordMapper.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import vip.xiaonuo.biz.modular.consumptionrecord.entity.ConsumptionRecord;
+import vip.xiaonuo.biz.modular.disablerecord.entity.BizDisableRecord;
+
+/**
+ * 清除记录Mapper接口
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+public interface BizDisableRecordMapper extends BaseMapper<BizDisableRecord> {
+    Page<BizDisableRecord> getPageList(@Param("page") Page<BizDisableRecord> page, @Param("ew") QueryWrapper<BizDisableRecord> ew);
+}

+ 17 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/mapper/mapping/BizDisableRecordMapper.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="vip.xiaonuo.biz.modular.disablerecord.mapper.BizDisableRecordMapper">
+
+    <select id="getPageList" resultType="vip.xiaonuo.biz.modular.disablerecord.entity.BizDisableRecord">
+        select
+            su.name userName,
+            su.phone,
+            bdr.account_balance,
+            bdr.voucher_balance,
+            bdr.coupon_code,
+            bdr.create_time
+        from biz_disable_record bdr
+        left join SYS_USER su on bdr.user_id = su.ID
+        ${ew.customSqlSegment}
+    </select>
+</mapper>

+ 50 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/param/BizDisableRecordAddParam.java

@@ -0,0 +1,50 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 清除记录添加参数
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+@Getter
+@Setter
+public class BizDisableRecordAddParam {
+
+    /** 用户ID */
+    @Schema(description = "用户ID")
+    private String userId;
+
+    /** 账户余额清除金额 */
+    @Schema(description = "账户余额清除金额")
+    private BigDecimal accountBalance;
+
+    /** 代金券清除金额 */
+    @Schema(description = "代金券清除金额")
+    private BigDecimal voucherBalance;
+
+    /** 清除蛋糕券编码 */
+    @Schema(description = "清除蛋糕券编码")
+    private String couponCode;
+
+}

+ 55 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/param/BizDisableRecordEditParam.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 清除记录编辑参数
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+@Getter
+@Setter
+public class BizDisableRecordEditParam {
+
+    /** 主键ID */
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "id不能为空")
+    private String id;
+
+    /** 用户ID */
+    @Schema(description = "用户ID")
+    private String userId;
+
+    /** 账户余额清除金额 */
+    @Schema(description = "账户余额清除金额")
+    private BigDecimal accountBalance;
+
+    /** 代金券清除金额 */
+    @Schema(description = "代金券清除金额")
+    private BigDecimal voucherBalance;
+
+    /** 清除蛋糕券编码 */
+    @Schema(description = "清除蛋糕券编码")
+    private String couponCode;
+
+}

+ 35 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/param/BizDisableRecordIdParam.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 清除记录Id参数
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+@Getter
+@Setter
+public class BizDisableRecordIdParam {
+
+    /** 主键ID */
+    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "id不能为空")
+    private String id;
+}

+ 53 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/param/BizDisableRecordPageParam.java

@@ -0,0 +1,53 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.param;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 清除记录查询参数
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+@Getter
+@Setter
+public class BizDisableRecordPageParam {
+
+    /** 当前页 */
+    @Schema(description = "当前页码")
+    private Integer current;
+
+    /** 每页条数 */
+    @Schema(description = "每页条数")
+    private Integer size;
+
+    /** 排序字段 */
+    @Schema(description = "排序字段,字段驼峰名称,如:userName")
+    private String sortField;
+
+    /** 排序方式 */
+    @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND")
+    private String sortOrder;
+
+    /** 关键词 */
+    @Schema(description = "关键词")
+    private String searchKey;
+
+    private String userId;
+
+}

+ 80 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/service/BizDisableRecordService.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import vip.xiaonuo.biz.modular.disablerecord.entity.BizDisableRecord;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordAddParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordEditParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordIdParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordPageParam;
+
+import java.util.List;
+
+/**
+ * 清除记录Service接口
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+public interface BizDisableRecordService extends IService<BizDisableRecord> {
+
+    /**
+     * 获取清除记录分页
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    Page<BizDisableRecord> page(BizDisableRecordPageParam bizDisableRecordPageParam);
+
+    /**
+     * 添加清除记录
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    void add(BizDisableRecordAddParam bizDisableRecordAddParam);
+
+    /**
+     * 编辑清除记录
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    void edit(BizDisableRecordEditParam bizDisableRecordEditParam);
+
+    /**
+     * 删除清除记录
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    void delete(List<BizDisableRecordIdParam> bizDisableRecordIdParamList);
+
+    /**
+     * 获取清除记录详情
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     */
+    BizDisableRecord detail(BizDisableRecordIdParam bizDisableRecordIdParam);
+
+    /**
+     * 获取清除记录详情
+     *
+     * @author fanzherong
+     * @date  2025/02/14 11:33
+     **/
+    BizDisableRecord queryEntity(String id);
+}

+ 99 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/disablerecord/service/impl/BizDisableRecordServiceImpl.java

@@ -0,0 +1,99 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.biz.modular.disablerecord.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import vip.xiaonuo.common.enums.CommonSortOrderEnum;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.page.CommonPageRequest;
+import vip.xiaonuo.biz.modular.disablerecord.entity.BizDisableRecord;
+import vip.xiaonuo.biz.modular.disablerecord.mapper.BizDisableRecordMapper;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordAddParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordEditParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordIdParam;
+import vip.xiaonuo.biz.modular.disablerecord.param.BizDisableRecordPageParam;
+import vip.xiaonuo.biz.modular.disablerecord.service.BizDisableRecordService;
+import vip.xiaonuo.common.util.CommonCryptogramUtil;
+
+import java.util.List;
+
+/**
+ * 清除记录Service接口实现类
+ *
+ * @author fanzherong
+ * @date  2025/02/14 11:33
+ **/
+@Service
+public class BizDisableRecordServiceImpl extends ServiceImpl<BizDisableRecordMapper, BizDisableRecord> implements BizDisableRecordService {
+
+    @Override
+    public Page<BizDisableRecord> page(BizDisableRecordPageParam bizDisableRecordPageParam) {
+        QueryWrapper<BizDisableRecord> queryWrapper = new QueryWrapper<BizDisableRecord>().checkSqlInjection();
+        if(ObjectUtil.isNotEmpty(bizDisableRecordPageParam.getUserId())){
+            queryWrapper.eq("bdr.user_id",bizDisableRecordPageParam.getUserId());
+        }
+        queryWrapper.eq("bdr.delete_flag","NOT_DELETE");
+        queryWrapper.orderByDesc("bdr.create_time");
+        Page<BizDisableRecord> pageList = this.getBaseMapper().getPageList(CommonPageRequest.defaultPage(), queryWrapper);
+        for(BizDisableRecord bizDisableRecord : pageList.getRecords()){
+            if(ObjectUtil.isNotEmpty(bizDisableRecord.getPhone())){
+                bizDisableRecord.setPhone(CommonCryptogramUtil.doSm4CbcDecrypt(bizDisableRecord.getPhone()));
+            }
+        }
+        return pageList;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void add(BizDisableRecordAddParam bizDisableRecordAddParam) {
+        BizDisableRecord bizDisableRecord = BeanUtil.toBean(bizDisableRecordAddParam, BizDisableRecord.class);
+        this.save(bizDisableRecord);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void edit(BizDisableRecordEditParam bizDisableRecordEditParam) {
+        BizDisableRecord bizDisableRecord = this.queryEntity(bizDisableRecordEditParam.getId());
+        BeanUtil.copyProperties(bizDisableRecordEditParam, bizDisableRecord);
+        this.updateById(bizDisableRecord);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void delete(List<BizDisableRecordIdParam> bizDisableRecordIdParamList) {
+        // 执行删除
+        this.removeByIds(CollStreamUtil.toList(bizDisableRecordIdParamList, BizDisableRecordIdParam::getId));
+    }
+
+    @Override
+    public BizDisableRecord detail(BizDisableRecordIdParam bizDisableRecordIdParam) {
+        return this.queryEntity(bizDisableRecordIdParam.getId());
+    }
+
+    @Override
+    public BizDisableRecord queryEntity(String id) {
+        BizDisableRecord bizDisableRecord = this.getById(id);
+        if(ObjectUtil.isEmpty(bizDisableRecord)) {
+            throw new CommonException("清除记录不存在,id值为:{}", id);
+        }
+        return bizDisableRecord;
+    }
+}

+ 11 - 4
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/controller/BizRechargePlanConfigController.java

@@ -13,8 +13,11 @@
 package vip.xiaonuo.biz.modular.rechargeplanconfig.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.qcloud.cos.internal.crypto.QCLOUDKMS;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.validation.annotation.Validated;
@@ -166,10 +169,14 @@ public class BizRechargePlanConfigController {
      */
     @Operation(summary = "获取充值方案配置分页")
     @GetMapping("/biz/rechargeplanconfig/list")
-    public CommonResult<List<BizRechargePlanConfig>> list() {
-        List<BizRechargePlanConfig> planConfigs = bizRechargePlanConfigService.list(new LambdaQueryWrapper<BizRechargePlanConfig>()
-                .eq(BizRechargePlanConfig::getStatus, "ENABLE")
-                .orderByDesc(CommonEntity::getCreateTime));
+    public CommonResult<List<BizRechargePlanConfig>> list(BizRechargePlanConfigPageParam bizRechargePlanConfigPageParam) {
+        QueryWrapper<BizRechargePlanConfig> queryWrapper = new QueryWrapper<>();
+        if(ObjectUtil.isNotEmpty(bizRechargePlanConfigPageParam.getOrgId())){
+            queryWrapper.lambda().eq(BizRechargePlanConfig::getOrgId,bizRechargePlanConfigPageParam.getOrgId());
+        }
+        queryWrapper.lambda().eq(BizRechargePlanConfig::getStatus, "ENABLE");
+        queryWrapper.lambda().orderByDesc(CommonEntity::getCreateTime);
+        List<BizRechargePlanConfig> planConfigs = bizRechargePlanConfigService.list(queryWrapper);
         return CommonResult.data(planConfigs);
     }
 

+ 12 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/entity/BizRechargePlanConfig.java

@@ -61,5 +61,17 @@ public class BizRechargePlanConfig extends CommonEntity {
     @Schema(description = "状态:ENABLE.启用 DISABLED.停用")
     private String status;
 
+    /**门店id*/
+    private String orgId;
+
+    @TableField(exist = false)
+    private String orgName;
+
+    /**是否立即到账*/
+    private String isAccount;
+
+    /**帮助激活人数*/
+    private Integer helpCount;
+
 
 }

+ 5 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/mapper/BizRechargePlanConfigMapper.java

@@ -12,7 +12,11 @@
  */
 package vip.xiaonuo.biz.modular.rechargeplanconfig.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import vip.xiaonuo.biz.modular.disablerecord.entity.BizDisableRecord;
 import vip.xiaonuo.biz.modular.rechargeplanconfig.entity.BizRechargePlanConfig;
 
 /**
@@ -22,4 +26,5 @@ import vip.xiaonuo.biz.modular.rechargeplanconfig.entity.BizRechargePlanConfig;
  * @date  2025/02/04 16:32
  **/
 public interface BizRechargePlanConfigMapper extends BaseMapper<BizRechargePlanConfig> {
+    Page<BizRechargePlanConfig> getPageList(@Param("page") Page<BizRechargePlanConfig> page, @Param("ew") QueryWrapper<BizRechargePlanConfig> ew);
 }

+ 19 - 1
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/mapper/mapping/BizRechargePlanConfigMapper.xml

@@ -2,4 +2,22 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="vip.xiaonuo.biz.modular.rechargeplanconfig.mapper.BizRechargePlanConfigMapper">
 
-</mapper>
+    <select id="getPageList"
+            resultType="vip.xiaonuo.biz.modular.rechargeplanconfig.entity.BizRechargePlanConfig">
+        select
+            brpc.id,
+            brpc.recharge_amount,
+            brpc.coupon_amount,
+            brpc.coupon_num,
+            brpc.account_balance,
+            brpc.rebate_ratio,
+            brpc.status,
+            brpc.org_id,
+            brpc.is_account,
+            brpc.help_count,
+            so.name orgName
+        from biz_recharge_plan_config brpc
+        left join SYS_ORG so on brpc.org_id = so.ID
+        ${ew.customSqlSegment}
+    </select>
+</mapper>

+ 9 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/param/BizRechargePlanConfigAddParam.java

@@ -51,4 +51,13 @@ public class BizRechargePlanConfigAddParam {
     @Schema(description = "返点比例(代金券)")
     private BigDecimal rebateRatio;
 
+    /**门店id*/
+    private String orgId;
+
+    /**是否立即到账*/
+    private String isAccount;
+
+    /**帮助激活人数*/
+    private Integer helpCount;
+
 }

+ 9 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/param/BizRechargePlanConfigEditParam.java

@@ -56,4 +56,13 @@ public class BizRechargePlanConfigEditParam {
     @Schema(description = "返点比例(代金券)")
     private BigDecimal rebateRatio;
 
+    /**门店id*/
+    private String orgId;
+
+    /**是否立即到账*/
+    private String isAccount;
+
+    /**帮助激活人数*/
+    private Integer helpCount;
+
 }

+ 2 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/param/BizRechargePlanConfigPageParam.java

@@ -52,4 +52,6 @@ public class BizRechargePlanConfigPageParam {
     @Schema(description = "状态")
     private String status;
 
+    private String orgId;
+
 }

+ 14 - 3
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargeplanconfig/service/impl/BizRechargePlanConfigServiceImpl.java

@@ -19,6 +19,7 @@ import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import vip.xiaonuo.biz.modular.user.enums.BizUserStatusEnum;
@@ -48,16 +49,23 @@ public class BizRechargePlanConfigServiceImpl extends ServiceImpl<BizRechargePla
     public Page<BizRechargePlanConfig> page(BizRechargePlanConfigPageParam bizRechargePlanConfigPageParam) {
         QueryWrapper<BizRechargePlanConfig> queryWrapper = new QueryWrapper<BizRechargePlanConfig>().checkSqlInjection();
         if (ObjectUtil.isNotEmpty(bizRechargePlanConfigPageParam.getStatus())) {
-            queryWrapper.lambda().eq(BizRechargePlanConfig::getStatus, bizRechargePlanConfigPageParam.getStatus());
+            queryWrapper.eq("brpc.status", bizRechargePlanConfigPageParam.getStatus());
         }
-        queryWrapper.lambda().orderByDesc(BizRechargePlanConfig::getCreateTime);
-        return this.page(CommonPageRequest.defaultPage(), queryWrapper);
+        if (ObjectUtil.isNotEmpty(bizRechargePlanConfigPageParam.getOrgId())){
+            queryWrapper.eq("brpc.org_id",bizRechargePlanConfigPageParam.getOrgId());
+        }
+        queryWrapper.eq("brpc.delete_flag","NOT_DELETE");
+        queryWrapper.orderByDesc("brpc.create_time");
+        return this.getBaseMapper().getPageList(CommonPageRequest.defaultPage(), queryWrapper);
     }
 
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void add(BizRechargePlanConfigAddParam bizRechargePlanConfigAddParam) {
         BizRechargePlanConfig bizRechargePlanConfig = BeanUtil.toBean(bizRechargePlanConfigAddParam, BizRechargePlanConfig.class);
+        if(ObjectUtil.isEmpty(bizRechargePlanConfigAddParam.getHelpCount())){
+            bizRechargePlanConfig.setHelpCount(1);
+        }
         this.save(bizRechargePlanConfig);
     }
 
@@ -66,6 +74,9 @@ public class BizRechargePlanConfigServiceImpl extends ServiceImpl<BizRechargePla
     public void edit(BizRechargePlanConfigEditParam bizRechargePlanConfigEditParam) {
         BizRechargePlanConfig bizRechargePlanConfig = this.queryEntity(bizRechargePlanConfigEditParam.getId());
         BeanUtil.copyProperties(bizRechargePlanConfigEditParam, bizRechargePlanConfig);
+        if(StringUtils.equals(bizRechargePlanConfigEditParam.getIsAccount(),"1")){
+            bizRechargePlanConfig.setHelpCount(1);
+        }
         this.updateById(bizRechargePlanConfig);
     }
 

+ 8 - 8
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargerecord/mapper/mapping/BizRechargeRecordMapper.xml

@@ -6,25 +6,25 @@
         SELECT
             t.id,
             t.user_id,
-            su.NAME,
-            su.PHONE,
+            u.NAME,
+            u.PHONE,
             t.recharge_plan_id,
             t.plan_account_balance,
             t.recharge_amount,
             t.recharge_time,
-            t.old_account_balance,
-            t.old_voucher_balance,
-            t.new_account_balance,
-            t.new_voucher_balance,
+            IFNULL(t.old_account_balance,0) old_account_balance,
+            IFNULL(t.old_voucher_balance,0) old_voucher_balance,
+            IFNULL(t.new_account_balance,0) new_account_balance,
+            IFNULL(t.new_voucher_balance,0) new_voucher_balance,
             t.recharge_plan_describe,
             t.order_no,
             t.wx_pay_amount,
             t.is_pay,
             t.pay_time
         FROM biz_recharge_record t
-        LEFT JOIN SYS_USER su ON t.user_id = su.id
+        LEFT JOIN SYS_USER u ON t.user_id = u.id
         <where>
             ${ew.sqlSegment}
         </where>
     </select>
-</mapper>
+</mapper>

+ 2 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargerecord/param/BizRechargeRecordPageParam.java

@@ -60,4 +60,6 @@ public class BizRechargeRecordPageParam {
     @Schema(description = "充值时间结束")
     private String endRechargeTime;
 
+    private Boolean isPay;
+
 }

+ 22 - 4
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/rechargerecord/service/impl/BizRechargeRecordServiceImpl.java

@@ -34,6 +34,7 @@ import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
 import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
@@ -89,7 +90,7 @@ public class BizRechargeRecordServiceImpl extends ServiceImpl<BizRechargeRecordM
     public Page<BizRechargeRecord> page(BizRechargeRecordPageParam bizRechargeRecordPageParam) {
         QueryWrapper<BizRechargeRecord> queryWrapper = new QueryWrapper<BizRechargeRecord>().checkSqlInjection();
         if (ObjectUtil.isNotEmpty(bizRechargeRecordPageParam.getSearchKey())) {
-            queryWrapper.and(s -> s.like("u.PHONE", bizRechargeRecordPageParam.getSearchKey())
+            queryWrapper.and(s -> s.like("u.ACCOUNT", bizRechargeRecordPageParam.getSearchKey())
                     .or().like("u.NAME", bizRechargeRecordPageParam.getSearchKey()));
         }
         if (ObjectUtil.isNotEmpty(bizRechargeRecordPageParam.getStartRechargeTime()) && ObjectUtil.isNotEmpty(bizRechargeRecordPageParam.getEndRechargeTime())) {
@@ -112,6 +113,13 @@ public class BizRechargeRecordServiceImpl extends ServiceImpl<BizRechargeRecordM
         if (ObjectUtil.isNotEmpty(bizRechargeRecordPageParam.getStartRechargeTime()) && ObjectUtil.isNotEmpty(bizRechargeRecordPageParam.getEndRechargeTime())) {
             queryWrapper.lambda().between(BizRechargeRecord::getRechargeTime, bizRechargeRecordPageParam.getStartRechargeTime(), bizRechargeRecordPageParam.getEndRechargeTime());
         }
+        if (ObjectUtil.isNotEmpty(bizRechargeRecordPageParam.getIsPay())){
+            if(bizRechargeRecordPageParam.getIsPay()){
+                queryWrapper.lambda().eq(BizRechargeRecord::getIsPay,1);
+            }else{
+                queryWrapper.lambda().eq(BizRechargeRecord::getIsPay,0);
+            }
+        }
         queryWrapper.lambda().eq(BizRechargeRecord::getUserId, StpLoginUserUtil.getLoginUser().getId());
         queryWrapper.lambda().orderByDesc(BizRechargeRecord::getRechargeTime);
         return this.page(CommonPageRequest.defaultPage(), queryWrapper);
@@ -131,6 +139,9 @@ public class BizRechargeRecordServiceImpl extends ServiceImpl<BizRechargeRecordM
         if (ObjectUtil.isNull(bizUser)) {
             throw new CommonException("未查询到该用户信息");
         }
+        if (StringUtils.equals(bizUser.getUserStatus(),"DISABLED")){
+            throw new CommonException("用户账号冻结,不可充值!");
+        }
         if (ObjectUtil.isNotEmpty(bizRechargeRecordAddParam.getRechargePlanId())) {
             // 校验充值方案
             BizRechargePlanConfig bizRechargePlanConfig = bizRechargePlanConfigMapper.selectById(bizRechargeRecordAddParam.getRechargePlanId());
@@ -148,9 +159,16 @@ public class BizRechargeRecordServiceImpl extends ServiceImpl<BizRechargeRecordM
                         .append(" x ").append(bizRechargePlanConfig.getCouponNum())
                         .append("(线下核销领蛋糕)");
             }
-            content.append(",账户余额到账").append(bizRechargePlanConfig.getAccountBalance())
-                    .append("元,返利").append(bizRechargePlanConfig.getRebateRatio())
-                    .append("%到推荐人代金券");
+            if(StringUtils.equals(bizRechargePlanConfig.getIsAccount(),"1")){
+                content.append(",账户余额到账").append(bizRechargePlanConfig.getAccountBalance())
+                        .append("元,返利").append(bizRechargePlanConfig.getRebateRatio())
+                        .append("%到推荐人代金券");
+            }else{
+                content.append(",账户余额到账").append(bizRechargePlanConfig.getAccountBalance())
+                        .append("元(待激活),返利").append(bizRechargePlanConfig.getRebateRatio())
+                        .append("%到推荐人代金券");
+            }
+
             bizRechargeRecord.setRechargePlanDescribe(content.toString());
             bizRechargeRecord.setRechargePlanId(bizRechargeRecordAddParam.getRechargePlanId());
             bizRechargeRecord.setPlanAccountBalance(bizRechargePlanConfig.getAccountBalance());

+ 8 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/recommendrecord/entity/BizRecommendRecord.java

@@ -46,11 +46,19 @@ public class BizRecommendRecord extends CommonEntity {
     @TableField(exist = false)
     private String recommendUserName;
 
+    /**推荐人手机号*/
+    @TableField(exist = false)
+    private String recommendUserPhone;
+
 
     /** 被推荐人用户id */
     @Schema(description = "被推荐人用户id")
     private String acceptUserId;
 
+    /**被推荐人手机号*/
+    @TableField(exist = false)
+    private String acceptUserPhone;
+
     /** 被推荐人 */
     @Schema(description = "被推荐人")
     @TableField(exist = false)

+ 4 - 2
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/recommendrecord/mapper/mapping/BizRecommendRecordMapper.xml

@@ -9,11 +9,13 @@
             t.accept_user_id,
             t.create_time,
             u1.`NAME` acceptUserName,
-            u2.`NAME` recommendUserName
+            u1.`PHONE` acceptUserPhone,
+            u2.`NAME` recommendUserName,
+            u2.`PHONE` recommendUserPhone
         FROM
             biz_recommend_record t
         LEFT JOIN SYS_USER u1 ON u1.ID = t.accept_user_id
         LEFT JOIN SYS_USER u2 ON u2.ID = t.recommend_user_id
         ${ew.customSqlSegment}
     </select>
-</mapper>
+</mapper>

+ 11 - 1
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/recommendrecord/service/impl/BizRecommendRecordServiceImpl.java

@@ -33,6 +33,7 @@ import vip.xiaonuo.biz.modular.recommendrecord.param.BizRecommendRecordEditParam
 import vip.xiaonuo.biz.modular.recommendrecord.param.BizRecommendRecordIdParam;
 import vip.xiaonuo.biz.modular.recommendrecord.param.BizRecommendRecordPageParam;
 import vip.xiaonuo.biz.modular.recommendrecord.service.BizRecommendRecordService;
+import vip.xiaonuo.common.util.CommonCryptogramUtil;
 
 import java.util.List;
 
@@ -54,7 +55,16 @@ public class BizRecommendRecordServiceImpl extends ServiceImpl<BizRecommendRecor
         }
         queryWrapper.eq("t.delete_flag", CommonDeleteFlagEnum.NOT_DELETE);
         queryWrapper.orderByAsc("t.create_time");
-        return baseMapper.page(CommonPageRequest.defaultPage(), queryWrapper);
+        Page<BizRecommendRecord> page = baseMapper.page(CommonPageRequest.defaultPage(), queryWrapper);
+        for(BizRecommendRecord bizRecommendRecord : page.getRecords()){
+            if(ObjectUtil.isNotEmpty(bizRecommendRecord.getRecommendUserPhone())){
+                bizRecommendRecord.setRecommendUserPhone(CommonCryptogramUtil.doSm4CbcDecrypt(bizRecommendRecord.getRecommendUserPhone()));
+            }
+            if(ObjectUtil.isNotEmpty(bizRecommendRecord.getAcceptUserPhone())){
+                bizRecommendRecord.setAcceptUserPhone(CommonCryptogramUtil.doSm4CbcDecrypt(bizRecommendRecord.getAcceptUserPhone()));
+            }
+        }
+        return page;
     }
 
     @Override

+ 45 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/task/couponRecordTask.java

@@ -0,0 +1,45 @@
+package vip.xiaonuo.biz.modular.task;
+
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import jakarta.annotation.Resource;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.biz.modular.couponrecord.entity.BizCouponRecord;
+import vip.xiaonuo.biz.modular.couponrecord.service.BizCouponRecordService;
+import vip.xiaonuo.common.timer.CommonTimerTaskRunner;
+
+import java.util.Date;
+import java.util.List;
+
+@Slf4j
+@Component
+public class couponRecordTask implements CommonTimerTaskRunner {
+
+    @Resource
+    private BizCouponRecordService bizCouponRecordService;
+
+    @Override
+    public void action(String extJson) {
+        log.info("=====蛋糕券检验开始=====");
+        //查询所有未核销的蛋糕券
+        List<BizCouponRecord> list = bizCouponRecordService.list(new QueryWrapper<BizCouponRecord>().lambda().eq(BizCouponRecord::getCouponStatus, "0"));
+        for(BizCouponRecord bizCouponRecord:list){
+            //判断蛋糕券是否过期
+            if(ObjectUtil.isNotEmpty(bizCouponRecord.getEndTime())){
+                if(new Date().getTime() > bizCouponRecord.getEndTime().getTime()){
+                    //已过期
+                    bizCouponRecord.setCouponStatus("2");
+                    bizCouponRecordService.updateById(bizCouponRecord);
+                    log.info("=====蛋糕券:"+bizCouponRecord.getCouponNo()+"已过期=====");
+                }
+            }
+        }
+    }
+
+
+}

+ 46 - 4
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/controller/BizUserController.java

@@ -14,7 +14,9 @@ package vip.xiaonuo.biz.modular.user.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import cn.hutool.core.lang.tree.Tree;
+import cn.hutool.json.JSONObject;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
@@ -23,10 +25,8 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotEmpty;
 import org.springframework.http.MediaType;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 import vip.xiaonuo.biz.modular.org.entity.BizOrg;
 import vip.xiaonuo.biz.modular.position.entity.BizPosition;
 import vip.xiaonuo.biz.modular.user.entity.BizUser;
@@ -157,6 +157,15 @@ public class BizUserController {
         return CommonResult.ok();
     }
 
+    @Operation(summary = "会员管理禁用人员")
+    @CommonLog("会员管理禁用人员")
+    @SaCheckPermission("/biz/user/disableCustomer")
+    @PostMapping("/biz/user/disableCustomer")
+    public CommonResult<String> disableCustomer(@RequestBody BizUserIdParam bizUserIdParam) {
+        bizUserService.disableCustomer(bizUserIdParam);
+        return CommonResult.ok();
+    }
+
     /**
      * 启用人员
      *
@@ -336,6 +345,39 @@ public class BizUserController {
         return CommonResult.ok();
     }
 
+    /***
+     * 下载导入模板
+     * @param response
+     * @throws IOException
+     */
+    @Operation(summary = "下载导入模板")
+    @GetMapping(value = "/biz/user/down", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+    public void downloadImportUserTemplate(HttpServletResponse response) throws IOException {
+        bizUserService.downloadImportTemplate(response);
+    }
+
+    /**
+     * 信息导入
+     * @param file
+     * @return
+     */
+    @ApiOperationSupport(order = 16)
+    @Operation(summary = "信息导入")
+    @PostMapping("/biz/user/import")
+    public CommonResult<JSONObject> importItem(@RequestPart("file") MultipartFile file) {
+        return CommonResult.data(bizUserService.importItem(file));
+    }
+
+    /**
+     * 根据用户id查询用户信息
+     */
+    @ApiOperationSupport(order = 17)
+    @Operation(summary = "根据用户id查询用户信息")
+    @GetMapping("/biz/user/getUserById")
+    public CommonResult<BizUser> getUserById(BizUserIdParam bizUserIdParam){
+        return CommonResult.data(bizUserService.queryEntity(bizUserIdParam.getId()));
+    }
+
 
 
 

+ 13 - 1
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/param/BizUserAddParam.java

@@ -17,6 +17,8 @@ import jakarta.validation.constraints.NotBlank;
 import lombok.Getter;
 import lombok.Setter;
 
+import java.math.BigDecimal;
+
 /**
  * 人员添加参数
  *
@@ -44,7 +46,7 @@ public class BizUserAddParam {
 
     /** 岗位id */
     @Schema(description = "岗位id", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotBlank(message = "positionId不能为空")
+    /*@NotBlank(message = "positionId不能为空")*/
     private String positionId;
 
     /** 岗级 */
@@ -174,4 +176,14 @@ public class BizUserAddParam {
     /** 扩展信息 */
     @Schema(description = "扩展信息")
     private String extJson;
+
+    /**用户类型: 1. 管理员 2.门店 3.会员*/
+    private String userType;
+
+    /**糕点(账户余额)*/
+    private BigDecimal accountBalance;
+
+    /**积分(代金券积分)*/
+    private BigDecimal voucherBalance;
+
 }

+ 20 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/param/BizUserImport.java

@@ -0,0 +1,20 @@
+package vip.xiaonuo.biz.modular.user.param;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class BizUserImport {
+    /**姓名*/
+    private String name;
+
+    /**手机号*/
+    private String phone;
+
+    /**糕点(账户余额)*/
+    private String accountBalance;
+
+    /**积分(代金券积分)*/
+    private String voucherBalance;
+}

+ 3 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/param/BizUserPageParam.java

@@ -53,4 +53,7 @@ public class BizUserPageParam {
     /** 所属机构 */
     @Schema(description = "所属机构")
     private String orgId;
+
+    private String beginTime;
+    private String endTime;
 }

+ 6 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/result/BizMemberUserResult.java

@@ -87,4 +87,10 @@ public class BizMemberUserResult extends CommonEntity {
     @Schema(description = "手机号")
     private String phone;
 
+    /**1:可冻结  2:不可冻结*/
+    private String disableFlag;
+
+    /**退款次数*/
+    private String count;
+
 }

+ 11 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/service/BizUserService.java

@@ -13,9 +13,11 @@
 package vip.xiaonuo.biz.modular.user.service;
 
 import cn.hutool.core.lang.tree.Tree;
+import cn.hutool.json.JSONObject;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.web.multipart.MultipartFile;
 import vip.xiaonuo.biz.modular.org.entity.BizOrg;
 import vip.xiaonuo.biz.modular.position.entity.BizPosition;
 import vip.xiaonuo.biz.modular.user.entity.BizUser;
@@ -57,6 +59,8 @@ public interface BizUserService extends IService<BizUser> {
      */
     void add(BizUserAddParam bizUserAddParam);
 
+    void addUser(BizUserAddParam bizUserAddParam);
+
     /**
      * 编辑人员
      *
@@ -97,6 +101,8 @@ public interface BizUserService extends IService<BizUser> {
      **/
     void disableUser(BizUserIdParam bizUserIdParam);
 
+    void disableCustomer(BizUserIdParam bizUserIdParam);
+
     /**
      * 启用人员
      *
@@ -195,4 +201,9 @@ public interface BizUserService extends IService<BizUser> {
 
 
     void smsSend(SmsSendParam smsSendParam);
+
+    /**导出模板*/
+    void downloadImportTemplate(HttpServletResponse response) throws IOException;
+
+    JSONObject importItem(MultipartFile file);
 }

+ 272 - 15
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/user/service/impl/BizUserServiceImpl.java

@@ -24,6 +24,7 @@ import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.img.ImgUtil;
 import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.lang.tree.Tree;
 import cn.hutool.core.lang.tree.TreeNode;
 import cn.hutool.core.lang.tree.TreeUtil;
@@ -31,6 +32,7 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.PhoneUtil;
 import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.excel.EasyExcel;
@@ -48,16 +50,30 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fhs.trans.service.impl.TransService;
+import com.google.common.collect.Maps;
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.compress.utils.Lists;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
+import org.springframework.web.multipart.MultipartFile;
 import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
 import vip.xiaonuo.biz.core.enums.BizBuildInEnum;
 import vip.xiaonuo.biz.core.enums.BizDataTypeEnum;
+import vip.xiaonuo.biz.modular.consumptionrecord.entity.ConsumptionRecord;
+import vip.xiaonuo.biz.modular.consumptionrecord.service.ConsumptionRecordService;
+import vip.xiaonuo.biz.modular.consumptionrecord.service.impl.ConsumptionRecordServiceImpl;
+import vip.xiaonuo.biz.modular.couponrecord.entity.BizCouponRecord;
+import vip.xiaonuo.biz.modular.couponrecord.param.BizCouponRecordPageParam;
+import vip.xiaonuo.biz.modular.couponrecord.service.BizCouponRecordService;
+import vip.xiaonuo.biz.modular.disablerecord.entity.BizDisableRecord;
+import vip.xiaonuo.biz.modular.disablerecord.service.BizDisableRecordService;
 import vip.xiaonuo.biz.modular.org.entity.BizOrg;
 import vip.xiaonuo.biz.modular.org.service.BizOrgService;
 import vip.xiaonuo.biz.modular.position.entity.BizPosition;
@@ -87,10 +103,8 @@ import vip.xiaonuo.dev.api.DevSmsApi;
 import vip.xiaonuo.sys.api.SysRoleApi;
 import vip.xiaonuo.sys.api.SysUserApi;
 
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
+import java.math.BigDecimal;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
@@ -134,6 +148,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
     private String templateCode;
     @Resource
     private BizRecommendRecordMapper bizRecommendRecordMapper;
+    @Resource
+    private ConsumptionRecordService consumptionRecordService;
+    @Resource
+    private BizDisableRecordService bizDisableRecordService;
+    @Resource
+    private BizCouponRecordService bizCouponRecordService;
 
 
     @Override
@@ -180,7 +200,10 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
         if (ObjectUtil.isNotEmpty(bizUserPageParam.getUserStatus())) {
             queryWrapper.lambda().eq(BizUser::getUserStatus, bizUserPageParam.getUserStatus());
         }
-        queryWrapper.lambda().eq(BizUser::getUserType, 3);
+        if (ObjectUtil.isNotEmpty(bizUserPageParam.getBeginTime()) && ObjectUtil.isNotEmpty(bizUserPageParam.getEndTime())) {
+            queryWrapper.lambda().between(BizUser::getCreateTime, bizUserPageParam.getBeginTime() + " 00:00:00", bizUserPageParam.getEndTime() + " 23:59:59");
+        }
+        queryWrapper.lambda().in(BizUser::getUserType, 3, 2);
         queryWrapper.lambda().eq(CommonEntity::getDeleteFlag, CommonDeleteFlagEnum.NOT_DELETE);
         queryWrapper.lambda().orderByDesc(CommonEntity::getCreateTime);
         Page<BizMemberUserResult> bizMemberUserResultPage = baseMapper.memberPage(CommonPageRequest.defaultPage(), queryWrapper);
@@ -188,6 +211,27 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
             if (ObjectUtil.isNotEmpty(bizMemberUserResult.getPhone())) {
                 bizMemberUserResult.setPhone(CommonCryptogramUtil.doSm4CbcDecrypt(bizMemberUserResult.getPhone()));
             }
+            String warnSwitch = devConfigApi.getValueByKey("warnSwitch");
+            if (ObjectUtil.isNotEmpty(warnSwitch) && StringUtils.equals(warnSwitch, "true")) {
+                //查询推荐的所有用户id
+                List<String> idList = Lists.newArrayList();
+                this.list(new QueryWrapper<BizUser>().lambda().eq(BizUser::getReferralUser, bizMemberUserResult.getId())).forEach(user -> idList.add(user.getId()));
+                if (ObjectUtil.isNotEmpty(idList)) {
+                    long count = consumptionRecordService.count(new QueryWrapper<ConsumptionRecord>().lambda().
+                            eq(ConsumptionRecord::getConsumptionOperate, "2").
+                            eq(ConsumptionRecord::getAdjustType, "2").
+                            in(ConsumptionRecord::getUserId, idList));
+                    if (count > Long.parseLong(devConfigApi.getValueByKey("warnCount"))) {
+                        //如果查询到的退款次数大于配置的次数,开始预警
+                        bizMemberUserResult.setDisableFlag("1");
+                    } else {
+                        bizMemberUserResult.setDisableFlag("2");
+                    }
+                }
+            } else {
+                bizMemberUserResult.setDisableFlag("2");
+            }
+
         }
         return bizMemberUserResultPage;
     }
@@ -205,12 +249,19 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
         bizUser.setPassword(CommonCryptogramUtil.doHashValue(devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_PASSWORD_KEY)));
         // 设置状态
         bizUser.setUserStatus(BizUserStatusEnum.ENABLE.getValue());
+        bizUser.setUserReferralCode(DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN) + RandomUtil.randomNumbers(4));
         this.save(bizUser);
 
         // 发布增加事件
         CommonDataChangeEventCenter.doAddWithData(BizDataTypeEnum.USER.getValue(), JSONUtil.createArray().put(bizUser));
     }
 
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void addUser(BizUserAddParam bizUserAddParam) {
+
+    }
+
     private void checkParam(BizUserAddParam bizUserAddParam) {
         // 校验数据范围
         List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
@@ -376,6 +427,49 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
                 bizUserIdParam.getId()).set(BizUser::getUserStatus, BizUserStatusEnum.DISABLED.getValue()));
     }
 
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void disableCustomer(BizUserIdParam bizUserIdParam) {
+        BizUser bizUser = this.detail(bizUserIdParam);
+        BigDecimal accountBalance = bizUser.getAccountBalance();
+        BigDecimal voucherBalance = bizUser.getVoucherBalance();
+        // 校验数据范围
+        List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
+        if (ObjectUtil.isNotEmpty(loginUserDataScope)) {
+            if (!loginUserDataScope.contains(bizUser.getOrgId())) {
+                throw new CommonException("您没有权限禁用该机构下的人员:{},机构id:{}", bizUser.getName(), bizUser.getOrgId());
+            }
+        } else {
+            if (!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
+                throw new CommonException("您没有权限禁用该机构下的人员:{},机构id:{}", bizUser.getName(), bizUser.getOrgId());
+            }
+        }
+        //账户余额和代金券金额清0
+        this.update(new LambdaUpdateWrapper<BizUser>().eq(BizUser::getId,
+                bizUserIdParam.getId()).set(BizUser::getUserStatus, BizUserStatusEnum.DISABLED.getValue())
+                .set(BizUser::getAccountBalance,new BigDecimal(0)).set(BizUser::getVoucherBalance,new BigDecimal(0)));
+
+        BizDisableRecord bizDisableRecord = new BizDisableRecord();
+        bizDisableRecord.setUserId(bizUser.getId());
+        bizDisableRecord.setAccountBalance(accountBalance);
+        bizDisableRecord.setVoucherBalance(voucherBalance);
+        BizCouponRecordPageParam bizCouponRecordPageParam = new BizCouponRecordPageParam();
+        bizCouponRecordPageParam.setUserId(bizUser.getId());
+        StringBuffer buffer = new StringBuffer();
+        List<BizCouponRecord> couponNoList = bizCouponRecordService.getList(bizCouponRecordPageParam);
+        //所有未核销的蛋糕券都核销掉
+        for(BizCouponRecord bizCouponRecord : couponNoList){
+            bizCouponRecord.setCouponStatus("1");
+            bizCouponRecord.setDestroyStatus(1);
+            buffer.append(bizCouponRecord.getCouponNo()+",");
+            bizCouponRecordService.updateById(bizCouponRecord);
+        }
+        if(ObjectUtil.isNotEmpty(buffer)){
+            bizDisableRecord.setCouponCode(buffer.substring(0,buffer.length()-1));
+        }
+        bizDisableRecordService.save(bizDisableRecord);
+    }
+
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void enableUser(BizUserIdParam bizUserIdParam) {
@@ -827,18 +921,41 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
                 throw new CommonException("未查询到该编码的推荐人");
             }
             referralUserId = referralUser.getId();
+            bizUser.setReferralUser(referralUserId);
         }
-        // 校验openId或者手机号是否存在
-        if (ObjectUtil.isNotNull(baseMapper.selectOne(new LambdaQueryWrapper<BizUser>()
+        // 直接校验openid是否重复,防止接口被重复强调用
+        BizUser openIdUser = baseMapper.selectOne(new LambdaQueryWrapper<BizUser>()
                 .eq(BizUser::getOpenId, wxUserRegisterParam.getOpenId())
-                .or()
-                .eq(BizUser::getAccount, wxUserRegisterParam.getPhone())
-                .last("limit 1")))) {
+                .last("limit 1"));
+        if (ObjectUtil.isNotNull(openIdUser)) {
+            log.error("触发openId相同校验 ,禁止注册 =========");
             throw new CommonException("该手机号已注册,请勿重复注册");
         }
+
+        BizUser oldUser = baseMapper.selectOne(new LambdaQueryWrapper<BizUser>()
+                .eq(BizUser::getAccount, wxUserRegisterParam.getPhone())
+                .last("limit 1"));
+        if (ObjectUtil.isNotNull(oldUser)) {
+            //校验手机号且openId已存在,不允许重复注册
+            if (ObjectUtil.isNotEmpty(oldUser.getOpenId())) {
+                log.error("触发手机号存在,但是已有openId校验,禁止注册 ==========");
+                throw new CommonException("该手机号已注册,请勿重复注册");
+            } else {
+                //无openId情况,后台导入的会员,直接更新绑定用户信息的openId
+                oldUser.setOpenId(wxUserRegisterParam.getOpenId());
+                baseMapper.updateById(oldUser);
+                //校验通过删除缓存
+                commonCacheOperator.remove(wxUserRegisterParam.getPhone());
+                //若填写了推荐人编码,绑定推荐人关系
+                if (ObjectUtil.isNotEmpty(wxUserRegisterParam.getReferralCode())) {
+                    binReferrer(referralUserId, oldUser.getId());
+                }
+                return;
+            }
+        }
         bizUser.setAccount(wxUserRegisterParam.getPhone());
         bizUser.setPhone(wxUserRegisterParam.getPhone());
-        bizUser.setAvatar(CommonAvatarUtil.generateImg(bizUser.getName()));
+        bizUser.setAvatar(CommonAvatarUtil.generateImg(wxUserRegisterParam.getName()));
         // 设置默认密码
         bizUser.setPassword(CommonCryptogramUtil.doHashValue(devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_PASSWORD_KEY)));
         // 设置状态
@@ -851,15 +968,25 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
         sysRoleApi.grantUserRole(bizUser.getId(), roleId);
         //保存推荐人关系
         if (ObjectUtil.isNotEmpty(wxUserRegisterParam.getReferralCode())) {
-            BizRecommendRecord bizRecommendRecord = new BizRecommendRecord();
-            bizRecommendRecord.setRecommendUserId(referralUserId);
-            bizRecommendRecord.setAcceptUserId(bizUser.getId());
-            bizRecommendRecordMapper.insert(bizRecommendRecord);
+            binReferrer(referralUserId, bizUser.getId());
         }
         //校验通过删除缓存
         commonCacheOperator.remove(wxUserRegisterParam.getPhone());
     }
 
+    /**
+     * 绑定推荐人
+     * @param referralUserId 推荐人id
+     * @param userId 被推荐人
+     */
+    private void binReferrer(String referralUserId, String userId) {
+        BizRecommendRecord bizRecommendRecord = new BizRecommendRecord();
+        bizRecommendRecord.setRecommendUserId(referralUserId);
+        bizRecommendRecord.setAcceptUserId(userId);
+        bizRecommendRecordMapper.insert(bizRecommendRecord);
+    }
+
+
     @Override
     public void smsSend(SmsSendParam smsSendParam) {
         // 查询缓存是否存在
@@ -873,4 +1000,134 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
         //保存5分钟
         commonCacheOperator.put(smsSendParam.getPhone(), code, 300);
     }
+
+    @Override
+    public void downloadImportTemplate(HttpServletResponse response) throws IOException {
+        try {
+            InputStream inputStream = POICacheManager.getFile("user.xlsx");
+            byte[] bytes = IoUtil.readBytes(inputStream);
+            CommonDownloadUtil.download("会员信息导入模板.xlsx", bytes, response);
+        } catch (Exception e) {
+            log.error(">>> 下载会员信息导入模板失败:", e);
+            CommonResponseUtil.renderError(response, "下载会员信息导入模板失败");
+        }
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public JSONObject importItem(MultipartFile file) {
+        try {
+            int successCount = 0;
+            int errorCount = 0;
+            JSONArray errorDetail = JSONUtil.createArray();
+            String fileName = file.getOriginalFilename();
+            // 创建临时文件
+            File tempFile = null;
+            if (fileName.endsWith(".xlsx")) {
+                tempFile = FileUtil.writeBytes(file.getBytes(), FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + "user.xlsx"));
+            } else if (fileName.endsWith(".xls")) {
+                tempFile = FileUtil.writeBytes(file.getBytes(), FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + "user.xlsx"));
+            } else {
+                throw new CommonException("导入文件格式不正确!");
+            }
+            // 读取excel
+            Integer excelRow = this.getExcelRow(tempFile);
+            List<BizUserImport> itemImportParamList = EasyExcel.read(tempFile).head(BizUserImport.class).sheet()
+                    .headRowNumber(excelRow).doReadSync();
+            for (int i = 0; i < itemImportParamList.size(); i++) {
+                JSONObject jsonObject = this.doImport(itemImportParamList.get(i), i);
+                if (jsonObject.getBool("success")) {
+                    successCount += 1;
+                } else {
+                    errorCount += 1;
+                    errorDetail.add(jsonObject);
+                }
+            }
+            return JSONUtil.createObj()
+                    .set("totalCount", itemImportParamList.size())
+                    .set("successCount", successCount).set("errorCount", errorCount).set("errorDetail", errorDetail);
+        } catch (Exception e) {
+            log.error(">>> 物资需求单导入失败:", e);
+            throw new CommonException("物资需求单导入失败");
+        }
+    }
+
+    //表头校验,获取读取的数据行数
+    private Integer getExcelRow(File file) {
+        FileInputStream input = null;
+
+        try {
+            input = new FileInputStream(file);
+            Workbook workbook = WorkbookFactory.create(input);
+            Sheet sheet = workbook.getSheetAt(0);
+
+            String headName = sheet.getRow(0).getCell(0).getStringCellValue();
+            //关闭流
+            input.close();
+            if ("日期".equals(headName)) {
+                return 1;
+            }
+        } catch (IOException e) {
+
+        }
+
+        return 2;
+    }
+
+    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
+    public JSONObject doImport(BizUserImport bizUserImport, int i) {
+        //姓名
+        String name = bizUserImport.getName();
+        //手机号码
+        String phone = bizUserImport.getPhone();
+        //糕点
+        String accountBalance = bizUserImport.getAccountBalance();
+        //积分
+        String voucherBalance = bizUserImport.getVoucherBalance();
+        if (ObjectUtil.hasEmpty(name,phone)) {
+            return JSONUtil.createObj().set("index", i + 1).set("success", false).set("msg", "必填字段存在空值");
+        }else{
+            try {
+                if (!PhoneUtil.isMobile(phone)) {
+                    return JSONUtil.createObj().set("index", i + 1).set("success", false).set("msg", "手机号码:"+phone+"格式错误");
+                }
+                if (this.count(new LambdaQueryWrapper<BizUser>()
+                        .eq(BizUser::getPhone, CommonCryptogramUtil.doSm4CbcEncrypt(phone))) > 0) {
+                    return JSONUtil.createObj().set("index", i + 1).set("success", false).set("msg", "存在重复的手机号,手机号为:"+phone);
+                }
+                if(!isNonNegativeInteger(accountBalance)){
+                    return JSONUtil.createObj().set("index", i + 1).set("success", false).set("msg", "糕点填写大于等于0的数字");
+                }
+                if(!isNonNegativeInteger(voucherBalance)){
+                    return JSONUtil.createObj().set("index", i + 1).set("success", false).set("msg", "积分填写大于等于0的数字");
+                }
+
+                BizUser bizUser = new BizUser();
+                BeanUtil.copyProperties(bizUserImport, bizUser);
+                bizUser.setAccount(phone);
+                bizUser.setOrgId("1543842934270394368");
+                bizUser.setUserType(3);
+                bizUser.setAccountBalance(new BigDecimal(accountBalance));
+                bizUser.setVoucherBalance(new BigDecimal(voucherBalance));
+                // 设置默认头像
+                bizUser.setAvatar(CommonAvatarUtil.generateImg(bizUser.getName()));
+                // 设置密码
+                bizUser.setPassword(CommonCryptogramUtil.doHashValue(devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_PASSWORD_KEY)));
+                // 设置状态
+                bizUser.setUserStatus(BizUserStatusEnum.ENABLE.getValue());
+                bizUser.setUserReferralCode(DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN) + RandomUtil.randomNumbers(4));
+                this.save(bizUser);
+                // 返回成功
+                return JSONUtil.createObj().set("success", true);
+            } catch (Exception e) {
+                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                log.error(">>> 数据导入异常:", e);
+                return JSONUtil.createObj().set("success", false).set("index", i + 1).set("msg", "数据导入异常:" + e);
+            }
+
+        }
+    }
+    public static boolean isNonNegativeInteger(String str) {
+        return str != null && str.matches("^\\d*\\.?\\d+$");
+    }
 }

+ 111 - 0
snowy-plugin/snowy-plugin-biz/src/main/java/vip/xiaonuo/biz/modular/utils/CommonExportUtil.java

@@ -0,0 +1,111 @@
+package vip.xiaonuo.biz.modular.utils;
+
+import cn.hutool.core.io.FileUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.write.handler.CellWriteHandler;
+import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.metadata.style.WriteFont;
+import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
+import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.ss.usermodel.*;
+import vip.xiaonuo.common.util.CommonDownloadUtil;
+import java.io.File;
+import java.util.List;
+
+public class CommonExportUtil {
+
+    public static void export(String fileName, Class c, List list, HttpServletResponse response , String sheetName){
+        File tempFile = null;
+        try {
+            // 创建临时文件
+            tempFile = FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
+
+            // 头的策略
+            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+            WriteFont headWriteFont = new WriteFont();
+            headWriteFont.setFontHeightInPoints((short) 14);
+            headWriteCellStyle.setWriteFont(headWriteFont);
+            // 水平垂直居中
+            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+            headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+            // 内容的策略
+            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
+            // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
+            contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
+            // 内容背景白色
+            contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
+            WriteFont contentWriteFont = new WriteFont();
+
+            // 内容字体大小
+            contentWriteFont.setFontHeightInPoints((short) 12);
+            contentWriteCellStyle.setWriteFont(contentWriteFont);
+
+            //设置边框样式,细实线
+            contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
+            contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
+            contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
+            contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
+
+            // 水平垂直居中
+            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
+            contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+            // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
+            HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle,
+                    contentWriteCellStyle);
+
+            // 写excel
+            EasyExcel.write(tempFile.getPath(), c)
+                    // 自定义样式
+                    .registerWriteHandler(horizontalCellStyleStrategy)
+                    // 自动列宽
+                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                    // 设置第一行字体
+                    .registerWriteHandler(new CellWriteHandler() {
+                        @Override
+                        public void afterCellDispose(CellWriteHandlerContext context) {
+                            WriteCellData<?> cellData = context.getFirstCellData();
+                            WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
+                            Integer rowIndex = context.getRowIndex();
+                            if(rowIndex == 0) {
+                                WriteFont headWriteFont = new WriteFont();
+                                headWriteFont.setFontName("宋体");
+                                headWriteFont.setBold(true);
+                                headWriteFont.setFontHeightInPoints((short) 16);
+                                writeCellStyle.setWriteFont(headWriteFont);
+                            }
+                        }
+                    })
+                    // 设置表头行高
+                    .registerWriteHandler(new AbstractRowHeightStyleStrategy() {
+                        @Override
+                        protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
+                            if(relativeRowIndex == 0) {
+                                // 表头第一行
+                                row.setHeightInPoints(34);
+                            } else {
+                                // 表头其他行
+                                row.setHeightInPoints(30);
+                            }
+                        }
+                        @Override
+                        protected void setContentColumnHeight(Row row, int relativeRowIndex) {
+                            // 内容行
+                            row.setHeightInPoints(20);
+                        }
+                    })
+                    .sheet(sheetName)
+                    .doWrite(list);
+            CommonDownloadUtil.download(tempFile, response);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            FileUtil.del(tempFile);
+        }
+    }
+}

Деякі файли не було показано, через те що забагато файлів було змінено