Browse Source

Merge branch 'feature/1219-tenant' into develop-new

yuanliang 1 year ago
parent
commit
3f0329a09e
39 changed files with 2470 additions and 324 deletions
  1. 14 3
      audio-analysis/src/main/java/com/yonge/netty/dto/WebSocketResponse.java
  2. 15 7
      audio-analysis/src/main/java/com/yonge/netty/server/handler/NettyServerHandler.java
  3. 2 2
      audio-analysis/src/main/java/com/yonge/netty/server/service/AudioCompareHandler.java
  4. 269 0
      audio-analysis/src/main/java/com/yonge/netty/server/service/DelayCheckHandler.java
  5. 1 1
      audio-analysis/src/main/java/com/yonge/netty/server/service/PitchDetectionHandler.java
  6. 157 27
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/SysSuggestionController.java
  7. 147 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/SysSuggestionTypeController.java
  8. 129 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/TenantStaffController.java
  9. 138 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/UserTenantAlbumRecordController.java
  10. 55 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/SysSuggestionTypeVo.java
  11. 119 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/SysSuggestionVo.java
  12. 70 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/TenantStaffVo.java
  13. 131 0
      cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/UserTenantAlbumRecordVo.java
  14. 1 1
      cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/SysSuggestionController.java
  15. 71 117
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/SysSuggestion.java
  16. 51 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/SysSuggestionType.java
  17. 1 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/SourceTypeEnum.java
  18. 28 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/SysSuggestionMapper.java
  19. 27 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/SysSuggestionTypeMapper.java
  20. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/TenantStaffMapper.java
  21. 2 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/UserTenantAlbumRecordMapper.java
  22. 40 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/SysSuggestionService.java
  23. 44 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/SysSuggestionTypeService.java
  24. 7 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TenantStaffService.java
  25. 4 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/UserTenantAlbumRecordService.java
  26. 21 3
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentServiceImpl.java
  27. 88 13
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/SysSuggestionServiceImpl.java
  28. 65 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/SysSuggestionTypeServiceImpl.java
  29. 124 13
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TenantStaffServiceImpl.java
  30. 107 29
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/UserTenantAlbumRecordServiceImpl.java
  31. 89 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/SysSuggestionTypeWrapper.java
  32. 161 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/SysSuggestionWrapper.java
  33. 108 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantStaffWrapper.java
  34. 28 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/UserTenantAlbumRecordWrapper.java
  35. 62 92
      cooleshow-user/user-biz/src/main/resources/config/mybatis/SysSuggestionMapper.xml
  36. 26 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/SysSuggestionTypeMapper.xml
  37. 34 5
      cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantStaffMapper.xml
  38. 32 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/UserTenantAlbumRecordMapper.xml
  39. 1 1
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/SysSuggestionController.java

+ 14 - 3
audio-analysis/src/main/java/com/yonge/netty/dto/WebSocketResponse.java

@@ -13,8 +13,8 @@ public class WebSocketResponse<T> {
 		this.body = body;
 	}
 
-	public WebSocketResponse(String command, T body) {
-		this.header = new Head(command, HttpStatus.OK.value());
+	public WebSocketResponse(String type, String command, T body) {
+		this.header = new Head(type, command, HttpStatus.OK.value());
 		this.body = body;
 	}
 
@@ -37,12 +37,15 @@ public class WebSocketResponse<T> {
 	public static class Head {
 		private int status = HttpStatus.OK.value();
 		private String commond = "";
+		
+		private String type;
 
 		public Head() {
 
 		}
 
-		public Head(String commond, int status) {
+		public Head(String type, String commond, int status) {
+			this.type = type;
 			this.commond = commond;
 			this.status = status;
 		}
@@ -63,5 +66,13 @@ public class WebSocketResponse<T> {
 			this.commond = commond;
 		}
 
+		public String getType() {
+			return type;
+		}
+
+		public void setType(String type) {
+			this.type = type;
+		}
+
 	}
 }

+ 15 - 7
audio-analysis/src/main/java/com/yonge/netty/server/handler/NettyServerHandler.java

@@ -1,6 +1,15 @@
 package com.yonge.netty.server.handler;
 
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.stereotype.Component;
+
+import com.yonge.netty.server.service.DelayCheckHandler;
 import com.yonge.netty.server.service.UserChannelContextService;
+
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
@@ -8,13 +17,6 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.handler.codec.http.HttpHeaders;
 import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
 
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.oauth2.common.OAuth2AccessToken;
-import org.springframework.stereotype.Component;
-
 @Component
 @ChannelHandler.Sharable
 public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@@ -26,6 +28,10 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
 
 	@Autowired
 	private UserChannelContextService userChannelContextService;
+
+	@Autowired
+	private DelayCheckHandler delayCheckHandler;
+	
 	@Override
 	public void channelActive(ChannelHandlerContext ctx) {
 		// 从管理器中添加
@@ -35,6 +41,7 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
 	@Override
 	public void channelUnregistered(ChannelHandlerContext ctx) {
 		userChannelContextService.remove(ctx.channel());
+		delayCheckHandler.getUserABCMap().remove(ctx.channel());
 		// 从管理器中移除
 		channelManager.remove(ctx.channel());
 	}
@@ -43,6 +50,7 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
 	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
 		LOGGER.error("[exceptionCaught][连接({}) 发生异常]", ctx.channel().id(), cause);
 		userChannelContextService.remove(ctx.channel());
+		delayCheckHandler.getUserABCMap().remove(ctx.channel());
 		// 断开连接
 		ctx.channel().close();
 	}

+ 2 - 2
audio-analysis/src/main/java/com/yonge/netty/server/service/AudioCompareHandler.java

@@ -230,7 +230,7 @@ public class AudioCompareHandler implements MessageHandler {
 					sysMusicCompareRecordService.saveMusicCompareData(sysMusicCompareRecord);
 				}
 				
-				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>("overall", params);
+				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>(getAction(), "overall", params);
 
 				nettyChannelManager.sendTextMessage(user, resp);
 			}
@@ -350,7 +350,7 @@ public class AudioCompareHandler implements MessageHandler {
 				params.put("measureIndex", sectionIndex);
 				params.put("measureRenderIndex", channelContext.getCurrentMusicSection(null, sectionIndex).getMeasureRenderIndex());
 
-				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>("measureScore", params);
+				WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>(getAction(), "measureScore", params);
 
 				nettyChannelManager.sendTextMessage(user, resp);
 			}

+ 269 - 0
audio-analysis/src/main/java/com/yonge/netty/server/service/DelayCheckHandler.java

@@ -0,0 +1,269 @@
+package com.yonge.netty.server.service;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.sound.sampled.AudioFormat;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONPath;
+import com.yonge.audio.analysis.AudioFloatConverter;
+import com.yonge.audio.analysis.detector.YINPitchDetector;
+import com.yonge.audio.utils.ArrayUtil;
+import com.yonge.netty.dto.NoteFrequencyRange;
+import com.yonge.netty.dto.WebSocketResponse;
+import com.yonge.netty.server.handler.NettyChannelManager;
+import com.yonge.netty.server.handler.message.MessageHandler;
+import com.yonge.netty.server.processor.WaveformWriter;
+
+import io.netty.channel.Channel;
+
+@Service
+public class DelayCheckHandler implements MessageHandler {
+
+	private final static Logger LOGGER = LoggerFactory.getLogger(DelayCheckHandler.class);
+
+	private int standardFrequecy = 3000;
+
+	/**
+	 * @describe 采样率
+	 */
+	private float sampleRate = 44100;
+
+	/**
+	 * 每个采样大小(Bit)
+	 */
+	private int bitsPerSample = 16;
+
+	/**
+	 * 通道数
+	 */
+	private int channels = 1;
+	
+	private int bufferSize = 1024 * 1;
+
+	private boolean signed = true;
+
+	private boolean bigEndian = false;
+
+	private AudioFormat audioFormat = new AudioFormat(sampleRate, bitsPerSample, channels, signed, bigEndian);
+
+	private AudioFloatConverter converter = AudioFloatConverter.getConverter(audioFormat);
+	
+	private boolean isRecWav = false;
+
+	private ConcurrentMap<Channel, UserContext> userABCMap = new ConcurrentHashMap<Channel, UserContext>();
+
+	private String tmpFileDir = "/mdata/soundCompare/";
+
+	private SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmSS");
+
+	@Autowired
+	private NettyChannelManager nettyChannelManager;
+
+	@Override
+	public String getAction() {
+		return "DELAY_CHECK";
+	}
+
+	@Override
+	public boolean handleTextMessage(String userId, Channel channel, String jsonMsg) {
+
+		String command = (String) JSONPath.extract(jsonMsg, "$.header.commond");
+
+		UserContext userContext = null;
+
+		switch (command) {
+		case "recordStart":
+
+			userContext = new UserContext(0, false);
+
+			userABCMap.put(channel, userContext);
+			
+			JSONObject dataObj = JSONObject.parseObject(JSON.toJSONString(JSONPath.extract(jsonMsg, "$.body")));
+			
+			if(dataObj.get("HZ") != null) {
+				String hzStr = dataObj.get("HZ").toString();
+				standardFrequecy = Integer.parseInt(hzStr);
+			}
+
+			break;
+		case "recordEnd":
+
+			userContext = userABCMap.get(channel);
+
+			if (userContext == null) {
+				userContext = new UserContext(0, false);
+			}
+
+			WaveformWriter waveFileProcessor = userContext.getWaveformWriter();
+			if (waveFileProcessor != null) {
+				// 写文件头
+				waveFileProcessor.processingFinished();
+			}
+
+			userContext = userABCMap.get(channel);
+
+			if (userContext == null) {
+				userContext = new UserContext(0, false);
+			}
+
+			Map<String, Object> params = new HashMap<String, Object>();
+			params.put("firstNoteDelayDuration", userContext.getDelayDuration());
+
+			WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>(getAction(), command, params);
+
+			nettyChannelManager.sendTextMessage(userId, resp);
+
+			userContext.setDelayDuration(0);
+			userContext.setIsOver(false);
+			userContext.setWaveformWriter(null);
+			userContext.setChannelBufferBytes(new byte[0]);
+			userABCMap.put(channel, userContext);
+			break;
+
+		default:
+			break;
+		}
+
+		return true;
+	}
+
+	@Override
+	public boolean handleBinaryMessage(String userId, Channel channel, byte[] bytes) {
+
+		UserContext userContext = userABCMap.get(channel);
+
+		if (userContext == null) {
+			userContext = new UserContext(0, false);
+		}
+
+		// 写录音文件
+		if (isRecWav) {
+			WaveformWriter waveFileProcessor = userContext.getWaveformWriter();
+			if (waveFileProcessor == null) {
+				File file = new File(tmpFileDir + userId + "_CHECK_" + sdf.format(new Date()) + ".wav");
+				waveFileProcessor = new WaveformWriter(file.getAbsolutePath());
+				userContext.setWaveformWriter(waveFileProcessor);
+
+				userABCMap.put(channel, userContext);
+			}
+			waveFileProcessor.process(bytes);
+		}
+
+		if (userContext.isOver) {
+			return true;
+		}
+		
+		userContext.setChannelBufferBytes(ArrayUtil.mergeByte(userContext.getChannelBufferBytes(), bytes));
+
+		int totalLength = userContext.getChannelBufferBytes().length;
+		
+		while (totalLength >= bufferSize) {
+			byte[] bufferData = ArrayUtil.extractByte(userContext.getChannelBufferBytes(), 0, bufferSize - 1);
+
+			if (bufferSize != totalLength) {
+				userContext.setChannelBufferBytes(ArrayUtil.extractByte(userContext.getChannelBufferBytes(), bufferSize, totalLength - 1));
+			} else {
+				userContext.setChannelBufferBytes(new byte[0]);
+			}
+
+			float[] sampleFloats = new float[bufferSize / 2];
+
+			converter.toFloatArray(bufferData, sampleFloats);
+			
+			YINPitchDetector frequencyDetector = new YINPitchDetector(sampleFloats.length, audioFormat.getSampleRate());
+
+			int playFrequency = (int) frequencyDetector.getFrequency(sampleFloats);
+
+			// int amplitude = (int) Signals.decibels(samples);
+
+			double durationTime = 1000 * (sampleFloats.length * 2) / audioFormat.getSampleRate() / (audioFormat.getSampleSizeInBits() / 8);
+
+			double playTime = userContext.delayDuration;
+
+			playTime += durationTime;
+
+			LOGGER.info("DurationTime:{}	 playFrequency:{}  PlayTime:{}" ,durationTime,playFrequency,playTime);
+			
+			NoteFrequencyRange nfr = new NoteFrequencyRange(440, playFrequency);
+			
+			if (nfr.getMinFrequency() < standardFrequecy && nfr.getMaxFrequency() > standardFrequecy) {
+
+				userContext.setIsOver(true);
+				userABCMap.put(channel, userContext);
+				return true;
+			}
+
+			userContext.setDelayDuration(playTime);
+
+			totalLength = userContext.getChannelBufferBytes().length;
+		}
+
+		return true;
+	}
+
+	public ConcurrentMap<Channel, UserContext> getUserABCMap() {
+		return userABCMap;
+	}
+
+	class UserContext {
+
+		public UserContext(double delayDuration, boolean isOver) {
+			this.delayDuration = delayDuration;
+			this.isOver = isOver;
+		}
+
+		private double delayDuration;
+
+		private boolean isOver;
+		
+		private byte[] channelBufferBytes = new byte[0];
+
+		private WaveformWriter waveformWriter;
+
+		public double getDelayDuration() {
+			return delayDuration;
+		}
+
+		public void setDelayDuration(double delayDuration) {
+			this.delayDuration = delayDuration;
+		}
+
+		public boolean isOver() {
+			return isOver;
+		}
+
+		public void setIsOver(boolean isOver) {
+			this.isOver = isOver;
+		}
+
+		public byte[] getChannelBufferBytes() {
+			return channelBufferBytes;
+		}
+
+		public void setChannelBufferBytes(byte[] channelBufferBytes) {
+			this.channelBufferBytes = channelBufferBytes;
+		}
+
+		public WaveformWriter getWaveformWriter() {
+			return waveformWriter;
+		}
+
+		public void setWaveformWriter(WaveformWriter waveformWriter) {
+			this.waveformWriter = waveformWriter;
+		}
+	}
+
+}

+ 1 - 1
audio-analysis/src/main/java/com/yonge/netty/server/service/PitchDetectionHandler.java

@@ -80,7 +80,7 @@ public class PitchDetectionHandler implements MessageHandler {
 		Map<String, Object> params = new HashMap<String, Object>();
 		params.put("frequency", playFrequency);
 
-		WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>("checking", params);
+		WebSocketResponse<Map<String, Object>> resp = new WebSocketResponse<Map<String, Object>>(getAction(), "checking", params);
 
 		nettyChannelManager.sendTextMessage(userId, resp);
 

+ 157 - 27
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/SysSuggestionController.java

@@ -1,19 +1,43 @@
 package com.yonge.cooleshow.admin.controller;
 
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.microsvc.toolkit.common.response.paging.PageInfo;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.admin.io.request.SysSuggestionVo;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
-import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.entity.SysSuggestion;
-import com.yonge.cooleshow.biz.dal.queryInfo.SysSuggestionQueryInfo;
+import com.yonge.cooleshow.biz.dal.entity.SysSuggestionType;
+import com.yonge.cooleshow.biz.dal.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.service.SysSuggestionService;
+import com.yonge.cooleshow.biz.dal.service.SysSuggestionTypeService;
+import com.yonge.cooleshow.biz.dal.service.SysUserService;
+import com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionWrapper;
 import com.yonge.cooleshow.common.controller.BaseController;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
 @Api(tags = "意见反馈")
 @RestController
 @RequestMapping("${app-config.url.admin:}/")
@@ -21,39 +45,145 @@ public class SysSuggestionController extends BaseController {
 
     @Autowired
     private SysSuggestionService sysSuggestionService;
-    @Autowired
+    @Resource
     private SysUserFeignService sysUserFeignService;
 
-    @ApiOperation(value = "新增")
-    @RequestMapping("sysSuggestion/add")
-    @PreAuthorize("@pcs.hasPermissions('sysSuggestion/add')")
-    public Object add(SysSuggestion sysSuggestion) {
-        SysUser sysUser = sysUserFeignService.queryUserInfo();
-        if (sysUser == null) {
-            return failed("用户信息获取失败");
+    @Autowired
+    private SysUserService sysUserService;
+
+    @Autowired
+    private SysSuggestionTypeService sysSuggestionTypeService;
+
+    /**
+     * 查询单条
+     *
+     * @param id 详情ID
+     * @return R<SysSuggestionVo.SysSuggestion>
+     */
+    @ApiOperation(value = "详情", notes = "传入id")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestion/detail')")
+    @GetMapping("/detail/{id}")
+    public R<SysSuggestionWrapper.SysSuggestion> detail(@PathVariable("id") Long id) {
+
+        // 建议详情
+        SysSuggestion wrapper = sysSuggestionService.detail(id);
+        SysSuggestionWrapper.SysSuggestion suggestion =
+                SysSuggestionWrapper.SysSuggestion.from(JSON.toJSONString(wrapper));
+        if (wrapper != null && wrapper.getSuggestionTypeId() != null) {
+            SysSuggestionType suggestionType = sysSuggestionTypeService.getById(wrapper.getSuggestionTypeId());
+            if (suggestionType != null) {
+                suggestion.setSuggestionTypeName(suggestionType.getName());
+            }
+            com.yonge.cooleshow.auth.api.entity.SysUser createUser = sysUserService.getByUserId(wrapper.getUserId());
+            String clientType = wrapper.getClientType();
+            if (ClientEnum.STUDENT.getCode().equals(clientType) || ClientEnum.TENANT_STUDENT.getCode().equals(clientType)) {
+                suggestion.setNickname(createUser.getUsername());
+            } else {
+                suggestion.setNickname(createUser.getRealName());
+            }
+            Long handleBy = wrapper.getHandleBy();
+            if (handleBy != null) {
+                com.yonge.cooleshow.auth.api.entity.SysUser sysUser = sysUserService.getByUserId(handleBy);
+                suggestion.setHandleName(sysUser.getUsername());
+            }
         }
-        sysSuggestion.setUserId(sysUser.getId().longValue());
-        sysSuggestion.setClientType("TEACHER");
-        if(StringUtils.isEmpty(sysSuggestion.getMobileNo())){
-            sysSuggestion.setMobileNo(sysUser.getPhone());
+        return R.from(suggestion);
+    }
+
+
+    /**
+     * 查询分页
+     *
+     * @param query SysSuggestionWrapper.SysSuggestionQuery
+     * @return R<PageInfo < SysSuggestionWrapper.SysSuggestion>>
+     */
+    @ApiOperation(value = "查询分页")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestion/queryPage')")
+    @PostMapping("/queryPage")
+    public R<PageInfo<SysSuggestionWrapper.SysSuggestion>> page(@RequestBody SysSuggestionWrapper.SysSuggestionQuery query) {
+
+        IPage<SysSuggestionWrapper.SysSuggestion> pages = sysSuggestionService.selectPage(QueryInfo.getPage(query), query);
+
+        List<SysSuggestionWrapper.SysSuggestion> records = pages.getRecords();
+
+        if (CollectionUtils.isNotEmpty(records)) {
+            List<Long> handlerList = records.stream().map(SysSuggestionWrapper.SysSuggestion::getHandleBy).filter(Objects::nonNull).distinct().collect(Collectors.toList());
+            Map<Long, SysUser> mapByIds = new HashMap<>();
+            if (CollectionUtils.isNotEmpty(handlerList)) {
+                mapByIds.putAll(sysUserService.getMapByIds(handlerList));
+            }
+            for (SysSuggestionWrapper.SysSuggestion item : records) {
+                Long handleBy = item.getHandleBy();
+                if (handleBy != null && mapByIds.containsKey(handleBy)) {
+                    item.setHandleName(mapByIds.getOrDefault(handleBy, new SysUser()).getUsername());
+                }
+            }
         }
-        sysSuggestionService.insert(sysSuggestion);
-        return succeed();
+        return R.from(QueryInfo.pageInfo(pages, records));
+    }
+
+    /**
+     * 新增
+     *
+     * @param suggestion SysSuggestionVo.SysSuggestion
+     * @return R<Boolean>
+     */
+    @ApiOperation(value = "新增", notes = "传入sysSuggestion")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestion/page')")
+    @PostMapping("/add")
+    public R<Boolean> add(@Validated @RequestBody SysSuggestionVo.SysSuggestion suggestion,
+                          @RequestHeader(name = "user-agent") String userAgent) {
+        com.yonge.cooleshow.auth.api.entity.SysUser sysUser = sysUserFeignService.queryUserInfo();
+        suggestion.setUserId(sysUser.getId());
+        suggestion.setClientType(sysUser.getUserType());
+        suggestion.setUserAgent(userAgent);
+
+        return R.from(sysSuggestionService.save(JSON.parseObject(suggestion.jsonString(), SysSuggestion.class)));
     }
 
-    @ApiOperation(value = "删除")
-    @RequestMapping("sysSuggestion/del")
+    /**
+     * 修改
+     *
+     * @param suggestion SysSuggestionVo.SysSuggestion
+     * @return R<Boolean>
+     */
+    @ApiOperation(value = "修改", notes = "传入sysSuggestion")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestion/update')")
+//    @PostMapping("/update")
+    public R<Boolean> update(@Validated @RequestBody SysSuggestionVo.SysSuggestion suggestion) {
+        return R.from(sysSuggestionService.update(JSON.parseObject(suggestion.jsonString(), SysSuggestionWrapper.SysSuggestion.class)));
+    }
+
+    /**
+     * 删除
+     *
+     * @param id 详情ID
+     * @return R<Boolean>
+     */
+    @ApiOperation(value = "删除", notes = "传入id")
     @PreAuthorize("@pcs.hasPermissions('sysSuggestion/del')")
-    public Object del(Long id) {
-        sysSuggestionService.delete(id);
-        return succeed();
+//    @PostMapping("/del")
+    public R<Boolean> remove(@RequestParam Long id) {
+        return R.from(sysSuggestionService.removeById(id));
     }
 
-    @ApiOperation(value = "分页查询")
-    @RequestMapping("sysSuggestion/queryPage")
-    @PreAuthorize("@pcs.hasPermissions('sysSuggestion/queryPage')")
-    public Object queryPage(SysSuggestionQueryInfo queryInfo) {
-        return succeed(sysSuggestionService.queryPage(queryInfo));
+    @ApiOperation(value = "修改")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestion/handleSuggestion')")
+    @PostMapping("/handleSuggestion")
+    public R<Boolean> handleSuggestion(@Validated @RequestBody SysSuggestionWrapper.HandleSuggestion handleSuggestion) {
+        com.yonge.cooleshow.auth.api.entity.SysUser sysUser = sysUserFeignService.queryUserInfo();
+        handleSuggestion.setHandleBy(sysUser.getId());
+        return R.from(sysSuggestionService.handleSuggestion(handleSuggestion));
+    }
+
+    @ApiOperation(value = "查询未处理的建议数量")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestion/queryUnHandleNum')")
+    @GetMapping("/queryUnHandleNum")
+    public R<Integer> queryUnHandleNum() {
+        Integer count = sysSuggestionService.lambdaQuery()
+                .eq(SysSuggestion::getHandleStatus, false)
+                .count();
+        return R.from(count);
     }
 
 }

+ 147 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/SysSuggestionTypeController.java

@@ -0,0 +1,147 @@
+package com.yonge.cooleshow.admin.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.microsvc.toolkit.common.response.paging.PageInfo;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.microsvc.toolkit.common.webportal.exception.BizException;
+import com.yonge.cooleshow.admin.io.request.SysSuggestionTypeVo;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.SysSuggestionType;
+import com.yonge.cooleshow.biz.dal.service.SysSuggestionTypeService;
+import com.yonge.cooleshow.biz.dal.service.SysUserService;
+import com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionTypeWrapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/sysSuggestionType")
+@Api(tags = "平台建议类型表")
+public class SysSuggestionTypeController {
+
+    @Autowired
+    private SysSuggestionTypeService sysSuggestionTypeService;
+
+    @Resource
+    private SysUserFeignService sysUserFeignService;
+
+    @Autowired
+    private SysUserService sysUserService;
+
+    @ApiOperation(value = "详情", notes = "平台建议类型表-根据详情ID查询单条, 传入id")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestionType/detail', {'BACKEND'})")
+    @GetMapping("/detail/{id}")
+    public R<SysSuggestionType> detail(@PathVariable("id") Long id) {
+
+        SysSuggestionType wrapper = sysSuggestionTypeService.detail(id);
+
+        return R.from(wrapper);
+    }
+
+    @ApiOperation(value = "查询分页", notes = "平台建议类型表- 传入 SysSuggestionTypeWrapper.SysSuggestionTypeQuery")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestionType/page', {'BACKEND'})")
+    @PostMapping("/page")
+    public R<PageInfo<SysSuggestionTypeWrapper.SysSuggestionType>> page(@RequestBody SysSuggestionTypeWrapper.SysSuggestionTypeQuery query) {
+
+        IPage<SysSuggestionType> pages = sysSuggestionTypeService.selectPage(QueryInfo.getPage(query), query);
+
+        Map<Long, com.yonge.cooleshow.biz.dal.entity.SysUser> idEmpMap = new HashMap<>();
+        List<Long> updateIds = pages.getRecords().stream().map(SysSuggestionType::getUpdateBy)
+                .filter(Objects::nonNull).distinct().collect(Collectors.toList());
+        if (!updateIds.isEmpty()) {
+            idEmpMap.putAll(sysUserService.getMapByIds(updateIds));
+        }
+        List<SysSuggestionTypeWrapper.SysSuggestionType> rows = pages.getRecords().stream().map(next -> {
+            SysSuggestionTypeWrapper.SysSuggestionType type =
+                    JSON.parseObject(JSON.toJSONString(next), SysSuggestionTypeWrapper.SysSuggestionType.class);
+            if (type.getUpdateBy() != null) {
+                type.setOperator(idEmpMap.getOrDefault(type.getUpdateBy(), new com.yonge.cooleshow.biz.dal.entity.SysUser()).getUsername());
+            }
+            return type;
+        }).collect(Collectors.toList());
+        return R.from(QueryInfo.pageInfo(pages, rows));
+    }
+
+    @ApiOperation(value = "新增", notes = "平台建议类型表- 传入 SysSuggestionTypeWrapper.SysSuggestionType")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestionType/save', {'BACKEND'})")
+    @PostMapping("/save")
+    public R<JSONObject> add(@Validated @RequestBody SysSuggestionTypeVo.SysSuggestionType sysSuggestionType) {
+        Integer count = sysSuggestionTypeService.lambdaQuery()
+                .eq(SysSuggestionType::getName, sysSuggestionType.getName())
+                .eq(SysSuggestionType::getDelFlag, false)
+                .count();
+        if (count > 0) {
+            throw new BizException("建议类型已存在");
+        }
+        SysUser userInfo = sysUserFeignService.queryUserInfo();
+        SysSuggestionType suggestionType = new SysSuggestionType();
+        suggestionType.setName(sysSuggestionType.getName());
+        suggestionType.setCreateBy(userInfo.getId());
+        suggestionType.setUpdateBy(userInfo.getId());
+        suggestionType.setDelFlag(false);
+        // 新增数据
+        sysSuggestionTypeService.save(suggestionType);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "修改", notes = "平台建议类型表- 传入 SysSuggestionTypeWrapper.SysSuggestionType")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestionType/update', {'BACKEND'})")
+    @PostMapping("/update")
+    public R<JSONObject> update(@Validated @RequestBody SysSuggestionTypeVo.SysSuggestionType sysSuggestionType) {
+        SysUser userInfo = sysUserFeignService.queryUserInfo();
+        Integer count = sysSuggestionTypeService.lambdaQuery()
+                .eq(SysSuggestionType::getName, sysSuggestionType.getName())
+                .eq(SysSuggestionType::getDelFlag, false)
+                .ne(SysSuggestionType::getId, sysSuggestionType.getId())
+                .count();
+        if (count > 0) {
+            throw new BizException("建议类型已存在");
+        }
+
+        SysSuggestionType suggestionType = new SysSuggestionType();
+        suggestionType.setId(sysSuggestionType.getId());
+        suggestionType.setName(sysSuggestionType.getName());
+        suggestionType.setUpdateBy(userInfo.getId());
+        suggestionType.setDelFlag(false);
+        // 更新数据
+        sysSuggestionTypeService.updateById(suggestionType);
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "删除", notes = "平台建议类型表- 传入id")
+    @PreAuthorize("@pcs.hasPermissions('sysSuggestionType/remove', {'BACKEND'})")
+    @PostMapping("/remove")
+    public R<JSONObject> remove(@RequestParam Long id) {
+        sysSuggestionTypeService.lambdaUpdate()
+                .set(SysSuggestionType::getDelFlag, true)
+                .eq(SysSuggestionType::getId, id)
+                .update();
+        return R.defaultR();
+    }
+}

+ 129 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/TenantStaffController.java

@@ -0,0 +1,129 @@
+package com.yonge.cooleshow.admin.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.admin.io.request.TenantStaffVo;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.TenantStaff;
+import com.yonge.cooleshow.biz.dal.service.TenantStaffService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantStaffWrapper;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.toolset.base.page.PageInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/tenantStaff")
+@Api(tags = "机构员工表")
+public class TenantStaffController extends BaseController {
+
+    @Autowired
+    private TenantStaffService tenantStaffService;
+
+    @Resource
+    private SysUserFeignService sysUserFeignService;
+
+    @ApiOperation(value = "详情", notes = "机构员工表-根据详情ID查询单条, 传入id")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "id", dataType = "long")
+    })
+    @PreAuthorize("@pcs.hasPermissions('tenantStaff/detail', {'BACKEND'})")
+    @GetMapping("/detail/{id}")
+    public R<TenantStaffWrapper.TenantStaff> detail(@PathVariable("id") Long id) {
+        TenantStaff wrapper = tenantStaffService.detail(id);
+        if (wrapper == null) {
+            return R.from(new TenantStaffWrapper.TenantStaff());
+        }
+        TenantStaffWrapper.TenantStaff tenantStaff = TenantStaffWrapper.TenantStaff.from(JSON.toJSONString(wrapper));
+        SysUser sysUser = sysUserFeignService.queryUserById(wrapper.getUserId());
+        tenantStaff.setGender(sysUser.getGender());
+        tenantStaff.setNickname(sysUser.getUsername());
+        tenantStaff.setPhone(sysUser.getPhone());
+        tenantStaff.setBindStatus(StringUtils.isNotEmpty(tenantStaff.getWxOpenid()));
+        return R.from(tenantStaff);
+    }
+
+    @ApiOperation(value = "查询分页", notes = "机构员工表- 传入 TenantStaffVo.TenantStaffQuery")
+    @PreAuthorize("@pcs.hasPermissions('tenantStaff/page', {'BACKEND'})")
+    @PostMapping("/page")
+    public R<PageInfo<TenantStaffWrapper.TenantStaff>> page(@RequestBody TenantStaffWrapper.TenantStaffQuery query) {
+
+        // 查询数据
+        return R.from(PageUtil.pageInfo(tenantStaffService.selectPage(QueryInfo.getPage(query), query)));
+    }
+
+    @ApiOperation(value = "新增", notes = "机构员工表- 传入 TenantStaffVo.TenantStaff")
+    @PreAuthorize("@pcs.hasPermissions('tenantStaff/add', {'BACKEND'})")
+//    @PostMapping("/save")
+    public R<JSONObject> add(@Validated @RequestBody TenantStaffVo.TenantStaff tenantStaffVo) {
+
+        // 新增数据
+        tenantStaffService.save(JSON.parseObject(tenantStaffVo.jsonString(), TenantStaff.class));
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "修改", notes = "机构员工表- 传入 TenantStaffVo.TenantStaff")
+    @PreAuthorize("@pcs.hasPermissions('tenantStaff/update', {'BACKEND'})")
+    @PostMapping("/update")
+    public R<JSONObject> update(@Validated @RequestBody TenantStaffVo.TenantStaff tenantStaffVo) {
+
+        // 更新数据
+        tenantStaffService.update(JSON.parseObject(tenantStaffVo.jsonString(), TenantStaffWrapper.TenantStaffAdd.class));
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "启用/禁用", notes = "机构员工表- 传入 TenantStaff")
+    @PreAuthorize("@pcs.hasPermissions('tenantStaff/updateStatus', {'BACKEND'})")
+    @PostMapping("/updateStatus")
+    public R<JSONObject> updateStatus(@RequestBody TenantStaff vo) {
+
+        // 更新数据
+        tenantStaffService.updateStatus(vo.getId(), vo.getStatus());
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "清理wxOpenId")
+    @PreAuthorize("@pcs.hasPermissions('tenantStaff/clearWxOpenId', {'BACKEND'})")
+    @PostMapping("/clearWxOpenId")
+    public R<JSONObject> clearWxOpenId(@RequestBody TenantStaff vo) {
+
+        // 更新数据
+        tenantStaffService.clearWxOpenId(vo.getId());
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "删除", notes = "机构员工表- 传入id")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "id", dataType = "long")
+    })
+    @PreAuthorize("@pcs.hasPermissions('tenantStaff/remove', {'BACKEND'})")
+//    @PostMapping("/remove")
+    public R<Boolean> remove(@RequestParam Long id) {
+
+        return R.from(tenantStaffService.removeById(id));
+    }
+}

+ 138 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/controller/UserTenantAlbumRecordController.java

@@ -0,0 +1,138 @@
+package com.yonge.cooleshow.admin.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.microsvc.toolkit.common.response.template.R;
+import com.yonge.cooleshow.admin.io.request.UserTenantAlbumRecordVo;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.UserTenantAlbumRecord;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.SourceTypeEnum;
+import com.yonge.cooleshow.biz.dal.service.UserTenantAlbumRecordService;
+import com.yonge.cooleshow.biz.dal.wrapper.TenantAlbumWrapper;
+import com.yonge.cooleshow.biz.dal.wrapper.UserTenantAlbumRecordWrapper;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.base.page.PageInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.Redisson;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/userTenantAlbumRecord")
+@Api(tags = "购买训练工具记录")
+public class UserTenantAlbumRecordController extends BaseController {
+
+    @Autowired
+    private UserTenantAlbumRecordService userTenantAlbumRecordService;
+
+    @Resource
+    private SysUserFeignService sysUserFeignService;
+
+    @Autowired
+    private RedissonClient redissonClient;
+
+    @ApiOperation(value = "详情", notes = "购买训练工具记录-根据详情ID查询单条, 传入id")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "id", dataType = "long")
+    })
+    @PreAuthorize("@pcs.hasPermissions('userTenantAlbumRecord/detail', {'BACKEND'})")
+//    @GetMapping("/detail/{id}")
+    public R<UserTenantAlbumRecordVo.UserTenantAlbumRecord> detail(@PathVariable("id") Long id) {
+
+        UserTenantAlbumRecord wrapper = userTenantAlbumRecordService.detail(id);
+
+        return R.from(UserTenantAlbumRecordVo.UserTenantAlbumRecord.from(JSON.toJSONString(wrapper)));
+    }
+
+    @ApiOperation(value = "查询分页", notes = "购买训练工具记录- 传入 UserTenantAlbumRecordVo.UserTenantAlbumRecordQuery")
+    @PreAuthorize("@pcs.hasPermissions('userTenantAlbumRecord/page', {'BACKEND'})")
+    @PostMapping("/page")
+    public R<PageInfo<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord>> page(@RequestBody UserTenantAlbumRecordWrapper.UserTenantAlbumRecordQuery query) {
+        return R.from(PageUtil.pageInfo(userTenantAlbumRecordService.selectUserTenantAlbumRecordPage(QueryInfo.getPage(query), query)));
+    }
+
+    @ApiOperation(value = "新增", notes = "购买训练工具记录- 传入 UserTenantAlbumRecordVo.UserTenantAlbumRecord")
+    @PreAuthorize("@pcs.hasPermissions('userTenantAlbumRecord/save', {'BACKEND'})")
+    @PostMapping("/save")
+    public R<JSONObject> add(@Validated @RequestBody UserTenantAlbumRecordVo.UserTenantAlbumRecord userTenantAlbumRecordVo) {
+
+        // 新增数据
+        UserTenantAlbumRecord userTenantAlbumRecord = new UserTenantAlbumRecord();
+        userTenantAlbumRecord.setTenantId(userTenantAlbumRecordVo.getTenantId());
+        userTenantAlbumRecord.setTenantAlbumId(userTenantAlbumRecordVo.getTenantAlbumId());
+        userTenantAlbumRecord.setTimes(userTenantAlbumRecordVo.getTimes());
+        userTenantAlbumRecord.setType(userTenantAlbumRecordVo.getType());
+        userTenantAlbumRecord.setReason(userTenantAlbumRecordVo.getReason());
+        userTenantAlbumRecord.setUserId(userTenantAlbumRecordVo.getUserId());
+        userTenantAlbumRecord.setClientType(ClientEnum.STUDENT);
+        userTenantAlbumRecord.setSourceType(SourceTypeEnum.BACKEND_GIVE);
+
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        userTenantAlbumRecord.setCreateBy(sysUser.getId());
+
+        RLock lock = redissonClient.getLock("UserTenantAlbumRecord_" + userTenantAlbumRecordVo.getUserId() + "_" + userTenantAlbumRecordVo.getTenantAlbumId());
+        try {
+            boolean b = lock.tryLock(60, 60, TimeUnit.SECONDS);
+            if (b) {
+                userTenantAlbumRecordService.add(userTenantAlbumRecord);
+            } else {
+                throw new BizException("请勿重复点击");
+            }
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        } finally {
+            if (lock.getHoldCount() > 0) {
+                lock.unlock();
+            }
+        }
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "修改", notes = "购买训练工具记录- 传入 UserTenantAlbumRecordVo.UserTenantAlbumRecord")
+    @PreAuthorize("@pcs.hasPermissions('userTenantAlbumRecord/update', {'BACKEND'})")
+//    @PostMapping("/update")
+    public R<JSONObject> update(@Validated @RequestBody UserTenantAlbumRecordVo.UserTenantAlbumRecord userTenantAlbumRecordVo) {
+
+        // 更新数据
+        userTenantAlbumRecordService.updateById(JSON.parseObject(userTenantAlbumRecordVo.jsonString(), UserTenantAlbumRecord.class));
+
+        return R.defaultR();
+    }
+
+    @ApiOperation(value = "删除", notes = "购买训练工具记录- 传入id")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "id", dataType = "long")
+    })
+    @PreAuthorize("@pcs.hasPermissions('userTenantAlbumRecord/remove', {'BACKEND'})")
+//    @PostMapping("/remove")
+    public R<Boolean> remove(@RequestParam Long id) {
+
+        return R.from(userTenantAlbumRecordService.removeById(id));
+    }
+}

+ 55 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/SysSuggestionTypeVo.java

@@ -0,0 +1,55 @@
+package com.yonge.cooleshow.admin.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 平台建议类型表
+ * 2023-08-30 11:26:33
+ */
+@ApiModel(value = "SysSuggestionTypeVo对象", description = "平台建议类型表查询视图对象")
+public class SysSuggestionTypeVo {
+
+    @Data
+    @ApiModel(" SysSuggestionTypeQuery-平台建议类型表")
+    public static class SysSuggestionTypeQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static SysSuggestionTypeQuery from(String json) {
+            return JSON.parseObject(json, SysSuggestionTypeQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" SysSuggestionType-平台建议类型表")
+    public static class SysSuggestionType {
+
+
+        @ApiModelProperty("编号")
+        private Long id;
+
+        @ApiModelProperty("名称")
+        private String name;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static SysSuggestionType from(String json) {
+            return JSON.parseObject(json, SysSuggestionType.class);
+        }
+    }
+
+}

+ 119 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/SysSuggestionVo.java

@@ -0,0 +1,119 @@
+package com.yonge.cooleshow.admin.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Date;
+
+import lombok.Data;
+
+/**
+ * 平台建议表
+ * 2023-12-20 11:27:10
+ */
+@ApiModel(value = "SysSuggestionVo对象", description = "平台建议表查询视图对象")
+public class SysSuggestionVo {
+
+    @Data
+    @ApiModel(" SysSuggestionQuery-平台建议表")
+    public static class SysSuggestionQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static SysSuggestionQuery from(String json) {
+            return JSON.parseObject(json, SysSuggestionQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" SysSuggestion-平台建议表")
+    public static class SysSuggestion {
+
+
+        @ApiModelProperty("编号")
+        private Long id;
+
+
+        @ApiModelProperty("联系方式")
+        private String mobileNo;
+
+
+        @ApiModelProperty("标题")
+        private String title;
+
+
+        @ApiModelProperty("内容")
+        private String content;
+
+
+        @ApiModelProperty("")
+        private String url;
+
+
+        @ApiModelProperty("用户编号")
+        private Long userId;
+
+
+        @ApiModelProperty("提交时间")
+        private Date createTime;
+
+
+        @ApiModelProperty("")
+        private String clientId;
+
+
+        @ApiModelProperty("建议类型")
+        private String type;
+
+
+        @ApiModelProperty("")
+        private String clientType;
+
+
+        @ApiModelProperty("建议类型ID")
+        private Long suggestionTypeId;
+
+
+        @ApiModelProperty("客户端信息")
+        private String userAgent;
+
+
+        @ApiModelProperty("处理状态:true:已处理,false:待处理")
+        private Boolean handleStatus;
+
+
+        @ApiModelProperty("处理时间")
+        private Date handleTime;
+
+
+        @ApiModelProperty("处理人")
+        private Long handleBy;
+
+
+        @ApiModelProperty("是否反馈用户,true:是,false:否")
+        private Boolean feedback;
+
+
+        @ApiModelProperty("反馈内容")
+        private String feedbackContent;
+
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static SysSuggestion from(String json) {
+            return JSON.parseObject(json, SysSuggestion.class);
+        }
+    }
+
+}

+ 70 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/TenantStaffVo.java

@@ -0,0 +1,70 @@
+package com.yonge.cooleshow.admin.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Date;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 机构员工表
+ * 2023-12-19 09:49:04
+ */
+@ApiModel(value = "TenantStaffVo对象", description = "机构员工表查询视图对象")
+public class TenantStaffVo {
+
+    @Data
+    @ApiModel(" TenantStaffQuery-机构员工表")
+    public static class TenantStaffQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantStaffQuery from(String json) {
+            return JSON.parseObject(json, TenantStaffQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" TenantStaff-机构员工表")
+    public static class TenantStaff {
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+        @ApiModelProperty("机构ID")
+        private Long tenantId;
+
+        @ApiModelProperty("昵称")
+        private String nickname;
+
+        @ApiModelProperty("性别,0:女,1:男")
+        private Integer gender;
+
+        @ApiModelProperty("手机号")
+        private String phone;
+
+        @ApiModelProperty("是否是主管理员")
+        private Boolean manageAdmin;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantStaff from(String json) {
+            return JSON.parseObject(json, TenantStaff.class);
+        }
+    }
+
+}

+ 131 - 0
cooleshow-app/src/main/java/com/yonge/cooleshow/admin/io/request/UserTenantAlbumRecordVo.java

@@ -0,0 +1,131 @@
+package com.yonge.cooleshow.admin.io.request;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Date;
+
+import lombok.Data;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+
+/**
+ * 购买训练工具记录
+ * 2023-12-19 10:33:35
+ */
+@ApiModel(value = "UserTenantAlbumRecordVo对象", description = "购买训练工具记录查询视图对象")
+public class UserTenantAlbumRecordVo {
+
+    @Data
+    @ApiModel(" UserTenantAlbumRecordQuery-购买训练工具记录")
+    public static class UserTenantAlbumRecordQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static UserTenantAlbumRecordQuery from(String json) {
+            return JSON.parseObject(json, UserTenantAlbumRecordQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" UserTenantAlbumRecord-购买训练工具记录")
+    public static class UserTenantAlbumRecord {
+
+
+        @ApiModelProperty("记录id")
+        private Long id;
+
+
+        @ApiModelProperty(value = "用户id", required = true)
+        @NotNull
+        private Long userId;
+
+
+        @ApiModelProperty(value = "机构ID", required = true)
+        @NotNull
+        private Long tenantId;
+
+
+        @ApiModelProperty(value = "机构专辑ID", required = true)
+        @NotNull
+        private Long tenantAlbumId;
+
+
+        @ApiModelProperty("订单号")
+        private String orderNo;
+
+
+        @ApiModelProperty("ORDER:订单")
+        private String sourceType;
+
+
+        @ApiModelProperty("购买人员类型 TEACHRE :老师端 STUDNET : 学生端")
+        private String clientType;
+
+
+        @ApiModelProperty("订单详情号")
+        private String subOrderNo;
+
+
+        @ApiModelProperty("开始时间")
+        private Date startTime;
+
+
+        @ApiModelProperty("结束时间")
+        private Date endTime;
+
+
+        @ApiModelProperty("创建时间")
+        private Date createTime;
+
+
+        @ApiModelProperty("创建人")
+        private Long createBy;
+
+
+        @ApiModelProperty("更新时间")
+        private Date updateTime;
+
+
+        @ApiModelProperty(value = "时间类型 DAY:天 MONTH:月,YEAR:年", required = true)
+        @NotNull
+        @Pattern(regexp = "^DAY|MONTH|YEAR$")
+        private String type;
+
+
+        @ApiModelProperty(value = "添加时间数量", required = true)
+        @NotNull
+        @Max(value = 999)
+        private Integer times;
+
+
+        @ApiModelProperty("消息发送 0、未发送 1、已发送提前3天消息 2、已发送会员过期消息 暂时不用")
+        private Boolean msgStatus;
+
+
+        @ApiModelProperty("备注")
+        private String reason;
+
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static UserTenantAlbumRecord from(String json) {
+            return JSON.parseObject(json, UserTenantAlbumRecord.class);
+        }
+    }
+
+}

+ 1 - 1
cooleshow-app/src/main/java/com/yonge/cooleshow/teacher/controller/SysSuggestionController.java

@@ -34,7 +34,7 @@ public class SysSuggestionController extends BaseController {
         if(StringUtils.isEmpty(sysSuggestion.getMobileNo())){
             sysSuggestion.setMobileNo(sysUser.getPhone());
         }
-        sysSuggestionService.insert(sysSuggestion);
+        sysSuggestionService.save(sysSuggestion);
         return succeed();
     }
 

+ 71 - 117
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/SysSuggestion.java

@@ -1,135 +1,89 @@
 package com.yonge.cooleshow.biz.dal.entity;
 
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
-import org.apache.commons.lang3.builder.ToStringBuilder;
+import java.io.Serializable;
+import java.util.Date;
 
-import com.yonge.cooleshow.biz.dal.enums.SuggestionType;
-import com.yonge.cooleshow.common.entity.BaseEntity;
 
 /**
- * 对应数据库表(sys_suggestion):
+ * 平台建议表
+ * 2023-12-20 11:27:10
  */
-public class SysSuggestion extends BaseEntity {
+@Data
+@ApiModel(" SysSuggestion-平台建议表")
+@TableName("sys_suggestion")
+public class SysSuggestion implements Serializable {
 
-	/** 编号 */
+	@ApiModelProperty("编号")
+	@TableId(value = "id_")
 	private Long id;
-	
-	/** 联系方式 */
-	@ApiModelProperty(value = "联系方式",required = false)
+
+	@ApiModelProperty("联系方式")
+	@TableField(value = "mobile_no_")
 	private String mobileNo;
-	
-	/** 标题 */
-	@ApiModelProperty(value = "标题",required = false)
+
+	@ApiModelProperty("标题")
+	@TableField(value = "title_")
 	private String title;
-	
-	/** 内容 */
-	@ApiModelProperty(value = "内容",required = false)
+
+	@ApiModelProperty("内容")
+	@TableField(value = "content_")
 	private String content;
 
-	/** 媒体 */
-	@ApiModelProperty(value = "媒体",required = false)
+	@TableField(value = "url_")
 	private String url;
-	
-	/** 用户编号 */
-	@ApiModelProperty(value = "用户编号",required = false)
+
+	@ApiModelProperty("用户编号")
+	@TableField(value = "user_id_")
 	private Long userId;
-	
-	@ApiModelProperty(value = "客户端类型",required = false)
+
+	@ApiModelProperty("提交时间")
+	@TableField(value = "create_time_")
+	private Date createTime;
+
+	@TableField(value = "client_id_")
+	private String clientId;
+
+	@ApiModelProperty("建议类型")
+	@TableField(value = "type_")
+	private String type;
+
+	@TableField(value = "client_type_")
 	private String clientType;
 
-	@ApiModelProperty(value = "建议类型")
-	private SuggestionType type = SuggestionType.APP;
-
-	@ApiModelProperty(value = "用户名",required = false)
-	private String username;
-	
-	/** 提交时间 */
-	private java.util.Date createTime;
-
-	public String getUrl() {
-		return url;
-	}
-
-	public void setUrl(String url) {
-		this.url = url;
-	}
-
-	public String getUsername() {
-		return username;
-	}
-
-	public void setUsername(String username) {
-		this.username = username;
-	}
-
-	public void setId(Long id){
-		this.id = id;
-	}
-	
-	public Long getId(){
-		return this.id;
-	}
-			
-	public void setMobileNo(String mobileNo){
-		this.mobileNo = mobileNo;
-	}
-	
-	public String getMobileNo(){
-		return this.mobileNo;
-	}
-			
-	public void setTitle(String title){
-		this.title = title;
-	}
-	
-	public String getTitle(){
-		return this.title;
-	}
-			
-	public void setContent(String content){
-		this.content = content;
-	}
-	
-	public String getContent(){
-		return this.content;
-	}
-			
-	public void setUserId(Long userId){
-		this.userId = userId;
-	}
-	
-	public Long getUserId(){
-		return this.userId;
-	}
-			
-	public String getClientType() {
-		return clientType;
-	}
-
-	public void setClientType(String clientType) {
-		this.clientType = clientType;
-	}
-
-	public SuggestionType getType() {
-		return type;
-	}
-
-	public void setType(SuggestionType type) {
-		this.type = type;
-	}
-
-	public void setCreateTime(java.util.Date createTime){
-		this.createTime = createTime;
-	}
-	
-	public java.util.Date getCreateTime(){
-		return this.createTime;
-	}
-			
-	@Override
-	public String toString() {
-		return ToStringBuilder.reflectionToString(this);
-	}
-
-}
+	@ApiModelProperty("建议类型ID")
+	@TableField(value = "suggestion_type_id_")
+	private Long suggestionTypeId;
+
+	@ApiModelProperty("客户端信息")
+	@TableField(value = "user_agent_")
+	private String userAgent;
+
+	@ApiModelProperty("处理状态:true:已处理,false:待处理")
+	@TableField(value = "handle_status_")
+	private Boolean handleStatus;
+
+	@ApiModelProperty("处理时间")
+	@TableField(value = "handle_time_")
+	private Date handleTime;
+
+	@ApiModelProperty("处理人")
+	@TableField(value = "handle_by_")
+	private Long handleBy;
+
+	@ApiModelProperty("是否反馈用户,true:是,false:否")
+	@TableField(value = "feedback_")
+	private Boolean feedback;
+
+	@ApiModelProperty("反馈内容")
+	@TableField(value = "feedback_content_")
+	private String feedbackContent;
+
+}

+ 51 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/SysSuggestionType.java

@@ -0,0 +1,51 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 平台建议类型表
+ * 2023-08-30 11:26:33
+ */
+@Data
+@ApiModel(" SysSuggestionType-平台建议类型表")
+@TableName("sys_suggestion_type")
+public class SysSuggestionType implements Serializable {
+
+    @ApiModelProperty("编号")
+    @TableId(value = "id_")
+    private Long id;
+
+    @ApiModelProperty("名称")
+    @TableField(value = "name_")
+    private String name;
+
+    @ApiModelProperty("删除标识(0-正常,1-删除)")
+    @TableField(value = "del_flag_")
+    private Boolean delFlag;
+
+    @ApiModelProperty("创建人")
+    @TableField(value = "create_by_")
+    private Long createBy;
+
+    @ApiModelProperty("修改人")
+    @TableField(value = "update_by_")
+    private Long updateBy;
+
+    @ApiModelProperty("更新时间")
+    @TableField(value = "update_time_")
+    private Date updateTime;
+
+    @ApiModelProperty("创建时间")
+    @TableField(value = "create_time_")
+    private Date createTime;
+
+}

+ 1 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/SourceTypeEnum.java

@@ -18,6 +18,7 @@ public enum SourceTypeEnum implements BaseEnum<String, AuditStatusEnum> {
     PLATFORM("平台"),
     ACTIVITY("活动"),
     ORDER("订单"),
+    BACKEND_GIVE("后台赠送"),
 
 
     ;

+ 28 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/SysSuggestionMapper.java

@@ -0,0 +1,28 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.biz.dal.entity.SysSuggestion;
+import com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionWrapper;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * 平台建议表
+ * 2023-12-20 11:27:10
+ */
+@Repository
+public interface SysSuggestionMapper extends BaseMapper<SysSuggestion> {
+
+    /**
+     * 分页查询
+     * @param page IPage<SysSuggestionWrapper.SysSuggestion>
+     * @param param SysSuggestionWrapper.SysSuggestionQuery
+     * @return List<SysSuggestionWrapper.SysSuggestion>
+     */
+    List<SysSuggestionWrapper.SysSuggestion> selectPage(@Param("page") IPage<SysSuggestionWrapper.SysSuggestion> page, @Param("param") SysSuggestionWrapper.SysSuggestionQuery param);
+
+}

+ 27 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/SysSuggestionTypeMapper.java

@@ -0,0 +1,27 @@
+package com.yonge.cooleshow.biz.dal.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yonge.cooleshow.biz.dal.entity.SysSuggestionType;
+import com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionTypeWrapper;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * 平台建议类型表
+ * 2023-08-30 11:26:33
+ */
+@Repository
+public interface SysSuggestionTypeMapper extends BaseMapper<SysSuggestionType> {
+
+    /**
+     * 分页查询
+     * @param page IPage<SysSuggestionTypeWrapper.SysSuggestionType>
+     * @param param SysSuggestionTypeWrapper.SysSuggestionTypeQuery
+     * @return List<SysSuggestionTypeWrapper.SysSuggestionType>
+     */
+    List<SysSuggestionType> selectPage(@Param("page") IPage<SysSuggestionType> page, @Param("param") SysSuggestionTypeWrapper.SysSuggestionTypeQuery param);
+
+}

+ 1 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/TenantStaffMapper.java

@@ -22,7 +22,7 @@ public interface TenantStaffMapper extends BaseMapper<TenantStaff> {
 	 * @param param TenantStaffWrapper.TenantStaffQuery
 	 * @return List<TenantStaffWrapper.TenantStaff>
 	 */
-	List<TenantStaff> selectPage(@Param("page") IPage<TenantStaff> page, @Param("param") TenantStaffWrapper.TenantStaffQuery param);
+	List<TenantStaffWrapper.TenantStaff> selectPage(@Param("page") IPage<TenantStaffWrapper.TenantStaff> page, @Param("param") TenantStaffWrapper.TenantStaffQuery param);
 
     TenantStaff getByPhone(@Param("phone") String phone);
 

+ 2 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/mapper/UserTenantAlbumRecordMapper.java

@@ -28,6 +28,8 @@ public interface UserTenantAlbumRecordMapper extends BaseMapper<UserTenantAlbumR
      */
     List<TenantAlbumWrapper.TenantAlbum> selectPage(@Param("page") IPage<UserTenantAlbumRecord> page, @Param("param") UserTenantAlbumRecordWrapper.UserTenantAlbumRecordQuery param);
 
+    List<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord> selectUserTenantAlbumRecordPage(@Param("page") IPage<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord> page, @Param("param") UserTenantAlbumRecordWrapper.UserTenantAlbumRecordQuery param);
+
     List<Long> selectTenantIds(@Param("id") Long id);
 
     List<TenantAlbum> selectTenantAlbumInfo(@Param("tenantIds") List<Long> tenantIds);

+ 40 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/SysSuggestionService.java

@@ -1,8 +1,46 @@
 package com.yonge.cooleshow.biz.dal.service;
 
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
 import com.yonge.cooleshow.biz.dal.entity.SysSuggestion;
-import com.yonge.toolset.mybatis.service.BaseService;
+import com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionWrapper;
+
+/**
+ * 平台建议表
+ * 2023-12-20 11:27:10
+ */
+public interface SysSuggestionService extends IService<SysSuggestion> {
+
+    /**
+     * 查询详情
+     * @param id 详情ID
+     * @return SysSuggestion
+     */
+    SysSuggestion detail(Long id);
+
+    /**
+     * 分页查询
+     * @param page IPage<SysSuggestion>
+     * @param query SysSuggestionWrapper.SysSuggestionQuery
+     * @return IPage<SysSuggestion>
+     */
+    IPage<SysSuggestionWrapper.SysSuggestion> selectPage(IPage<SysSuggestionWrapper.SysSuggestion> page, SysSuggestionWrapper.SysSuggestionQuery query);
+
+    /**
+     * 添加
+     * @param sysSuggestion SysSuggestionWrapper.SysSuggestion
+     * @return Boolean
+     */
+    Boolean add(SysSuggestionWrapper.SysSuggestion sysSuggestion);
+
+    /**
+     * 更新
+     * @param sysSuggestion SysSuggestionWrapper.SysSuggestion
+     * @return Boolean
+     */
+    Boolean update(SysSuggestionWrapper.SysSuggestion sysSuggestion);
 
-public interface SysSuggestionService extends BaseService<Long, SysSuggestion> {
+    Boolean handleSuggestion(SysSuggestionWrapper.HandleSuggestion handleSuggestion);
 
 }

+ 44 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/SysSuggestionTypeService.java

@@ -0,0 +1,44 @@
+package com.yonge.cooleshow.biz.dal.service;
+
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yonge.cooleshow.biz.dal.entity.SysSuggestionType;
+import com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionTypeWrapper;
+
+/**
+ * 平台建议类型表
+ * 2023-08-30 11:26:33
+ */
+public interface SysSuggestionTypeService extends IService<SysSuggestionType> {
+
+    /**
+     * 查询详情
+     * @param id 详情ID
+     * @return SysSuggestionType
+     */
+    SysSuggestionType detail(Long id);
+
+    /**
+     * 分页查询
+     * @param page IPage<SysSuggestionType>
+     * @param query SysSuggestionTypeWrapper.SysSuggestionTypeQuery
+     * @return IPage<SysSuggestionType>
+     */
+    IPage<SysSuggestionType> selectPage(IPage<SysSuggestionType> page, SysSuggestionTypeWrapper.SysSuggestionTypeQuery query);
+
+    /**
+     * 添加
+     * @param sysSuggestionType SysSuggestionTypeWrapper.SysSuggestionType
+     * @return Boolean
+     */
+    Boolean add(SysSuggestionTypeWrapper.SysSuggestionType sysSuggestionType);
+
+    /**
+     * 更新
+     * @param sysSuggestionType SysSuggestionTypeWrapper.SysSuggestionType
+     * @return Boolean
+     */
+    Boolean update(SysSuggestionTypeWrapper.SysSuggestionType sysSuggestionType);
+
+}

+ 7 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/TenantStaffService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.yonge.cooleshow.biz.dal.entity.TenantStaff;
 import com.yonge.cooleshow.biz.dal.wrapper.TenantStaffWrapper;
+import com.yonge.cooleshow.common.enums.UserLockFlag;
 
 /**
  * 机构员工表
@@ -24,7 +25,7 @@ public interface TenantStaffService extends IService<TenantStaff>  {
      * @param query TenantStaffWrapper.TenantStaffQuery
      * @return IPage<TenantStaff>
      */
-    IPage<TenantStaff> selectPage(IPage<TenantStaff> page, TenantStaffWrapper.TenantStaffQuery query);
+    IPage<TenantStaffWrapper.TenantStaff> selectPage(IPage<TenantStaffWrapper.TenantStaff> page, TenantStaffWrapper.TenantStaffQuery query);
 	
     /**
      * 添加
@@ -38,7 +39,7 @@ public interface TenantStaffService extends IService<TenantStaff>  {
      * @param tenantStaff TenantStaffWrapper.TenantStaff
      * @return Boolean
      */
-     Boolean update(TenantStaffWrapper.TenantStaff tenantStaff);
+     Boolean update(TenantStaffWrapper.TenantStaffAdd tenantStaff);
 
     /**
      * 获取机构用户
@@ -54,4 +55,8 @@ public interface TenantStaffService extends IService<TenantStaff>  {
      * @return TenantStaff
      */
     TenantStaff getByUserId(Long userId);
+
+    void updateStatus(Long id, UserLockFlag status);
+
+    void clearWxOpenId(Long id);
 }

+ 4 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/UserTenantAlbumRecordService.java

@@ -30,13 +30,15 @@ public interface UserTenantAlbumRecordService extends IService<UserTenantAlbumRe
      * @return IPage<UserTenantAlbumRecord>
      */
     IPage<TenantAlbumWrapper.TenantAlbum> selectPage(IPage<TenantAlbumWrapper.TenantAlbum> page, UserTenantAlbumRecordWrapper.UserTenantAlbumRecordQuery query);
-	
+
+    IPage<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord> selectUserTenantAlbumRecordPage(IPage<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord> page, UserTenantAlbumRecordWrapper.UserTenantAlbumRecordQuery query);
+
     /**
      * 添加
      * @param userTenantAlbumRecord UserTenantAlbumRecordWrapper.UserTenantAlbumRecord
      * @return Boolean
      */
-     Boolean add(UserTenantAlbumRecordWrapper.UserTenantAlbumRecord userTenantAlbumRecord);   
+     Boolean add(UserTenantAlbumRecord userTenantAlbumRecord);
 
     /**
      * 更新

+ 21 - 3
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentServiceImpl.java

@@ -702,7 +702,7 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
             com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = updateAccount(studentInfo, oldSysUser);
             this.lambdaUpdate().set(Student::getSubjectId, studentInfo.getSubjectId())
                     .set(Student::getTenantId, studentInfo.getTenantId())
-                    .set(Student::getUserId, sysUser.getId())
+//                    .set(Student::getUserId, sysUser.getId())
                     .set(Student::getUpdateTime, new Date())
                     .set(Student::getTenantGroupId, studentInfo.getTenantGroupId())
                     .set(StringUtils.isNotEmpty(studentInfo.getAvatar()), Student::getAvatar, studentInfo.getAvatar())
@@ -931,8 +931,26 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
         if (!sysUsers.isEmpty()) {
             throw new BizException("手机号已经注册账号");
         } else {
-            oldSysUser.setPhone(studentInfo.getPhone());
-            com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = JSON.parseObject(JSON.toJSONString(oldSysUser), com.yonge.cooleshow.biz.dal.entity.SysUser.class);
+            com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = JSON.parseObject(JSON.toJSONString(studentInfo),
+                    com.yonge.cooleshow.biz.dal.entity.SysUser.class);
+            sysUser.setId(oldSysUser.getId());
+            sysUser.setPhone(studentInfo.getPhone());
+            sysUser.setAvatar(studentInfo.getAvatar());
+            if (StringUtils.isEmpty(studentInfo.getAvatar())) {
+                //设置默认头像
+                String avatar = sysConfigService.findConfigValue(SysConfigConstant.STUDENT_AVATAR);
+                sysUser.setAvatar(avatar);
+            }
+            sysUser.setGender(studentInfo.getGender());
+            sysUser.setBirthdate(studentInfo.getBirthdate());
+            sysUser.setUsername(studentInfo.getName());
+            String password = oldSysUser.getPassword();
+            sysUser.setPassword(password);
+
+            if (StringUtils.isEmpty(password)) {
+                String newPassword = MessageFormat.format("klx{0}", studentInfo.getPhone().substring(7));
+                sysUser.setPassword(new BCryptPasswordEncoder().encode(newPassword));
+            }
             sysUserMapper.updateById(sysUser);
             return sysUser;
         }

+ 88 - 13
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/SysSuggestionServiceImpl.java

@@ -1,23 +1,98 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
-import com.yonge.toolset.mybatis.service.impl.BaseServiceImpl;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import com.yonge.cooleshow.biz.dal.dao.SysSuggestionDao;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.microsvc.toolkit.common.webportal.exception.BizException;
 import com.yonge.cooleshow.biz.dal.entity.SysSuggestion;
+import com.yonge.cooleshow.biz.dal.mapper.SysSuggestionMapper;
 import com.yonge.cooleshow.biz.dal.service.SysSuggestionService;
-import com.yonge.toolset.mybatis.dal.BaseDAO;
+import com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionWrapper;
+import org.springframework.stereotype.Service;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.transaction.annotation.Transactional;
 
+import java.util.Date;
+
+/**
+ * 平台建议表
+ * 2023-12-20 11:27:10
+ */
+@Slf4j
 @Service
-public class SysSuggestionServiceImpl extends BaseServiceImpl<Long, SysSuggestion> implements SysSuggestionService {
-	
-	@Autowired
-	private SysSuggestionDao sysSuggestionDao;
+public class SysSuggestionServiceImpl extends ServiceImpl<SysSuggestionMapper, SysSuggestion> implements SysSuggestionService {
+
+	/**
+	 * 查询详情
+	 * @param id 详情ID
+	 * @return SysSuggestion
+	 */
+	@Override
+	public SysSuggestion detail(Long id) {
+
+		return baseMapper.selectById(id);
+	}
+
+	/**
+	 * 分页查询
+	 * @param page IPage<SysSuggestion>
+	 * @param query SysSuggestionWrapper.SysSuggestionQuery
+	 * @return IPage<SysSuggestion>
+	 */
+	@Override
+	public IPage<SysSuggestionWrapper.SysSuggestion> selectPage(IPage<SysSuggestionWrapper.SysSuggestion> page, SysSuggestionWrapper.SysSuggestionQuery query) {
+
+		return page.setRecords(baseMapper.selectPage(page, query));
+	}
+
+	/**
+	 * 添加
+	 * @param sysSuggestion SysSuggestionWrapper.SysSuggestion
+	 * @return Boolean
+	 */
+	@Override
+	public Boolean add(SysSuggestionWrapper.SysSuggestion sysSuggestion) {
+
+		return this.save(JSON.parseObject(sysSuggestion.jsonString(), SysSuggestion.class));
+	}
+
+	/**
+	 * 更新
+	 * @param sysSuggestion SysSuggestionWrapper.SysSuggestion
+	 * @return Boolean
+	 */
+	@Override
+	public Boolean update(SysSuggestionWrapper.SysSuggestion sysSuggestion){
+
+		return this.updateById(JSON.parseObject(sysSuggestion.jsonString(), SysSuggestion.class));
+	}
 
+	/**
+	 * 处理反馈意见
+	 * @param handleSuggestion handleSuggestion
+	 * @return true/false
+	 */
+	@Transactional(rollbackFor = Exception.class)
 	@Override
-	public BaseDAO<Long, SysSuggestion> getDAO() {
-		return sysSuggestionDao;
+	public Boolean handleSuggestion(SysSuggestionWrapper.HandleSuggestion handleSuggestion) {
+		Long id = handleSuggestion.getId();
+		SysSuggestion suggestion = this.getById(id);
+		if (suggestion == null) {
+			throw new BizException("反馈意见不存在");
+		}
+		Boolean handleStatus = suggestion.getHandleStatus();
+		if (Boolean.TRUE.equals(handleStatus)) {
+			throw new BizException("反馈意见已经处理");
+		}
+		this.lambdaUpdate()
+				.set(SysSuggestion::getHandleStatus, true)
+				.set(SysSuggestion::getHandleTime, new Date())
+				.set(SysSuggestion::getFeedback, handleSuggestion.getFeedback())
+				.set(SysSuggestion::getFeedbackContent, handleSuggestion.getFeedbackContent())
+				.set(SysSuggestion::getHandleBy,handleSuggestion.getHandleBy())
+				.eq(SysSuggestion::getId, handleSuggestion.getId())
+				.eq(SysSuggestion::getHandleStatus, false)
+				.update();
+		return true;
 	}
-	
 }

+ 65 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/SysSuggestionTypeServiceImpl.java

@@ -0,0 +1,65 @@
+package com.yonge.cooleshow.biz.dal.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.yonge.cooleshow.biz.dal.entity.SysSuggestionType;
+import com.yonge.cooleshow.biz.dal.mapper.SysSuggestionTypeMapper;
+import com.yonge.cooleshow.biz.dal.service.SysSuggestionTypeService;
+import com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionTypeWrapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * 平台建议类型表
+ * 2023-08-30 11:26:33
+ */
+@Slf4j
+@Service
+public class SysSuggestionTypeServiceImpl extends ServiceImpl<SysSuggestionTypeMapper, SysSuggestionType> implements SysSuggestionTypeService {
+
+    /**
+     * 查询详情
+     * @param id 详情ID
+     * @return SysSuggestionType
+     */
+    @Override
+    public SysSuggestionType detail(Long id) {
+
+        return baseMapper.selectById(id);
+    }
+
+    /**
+     * 分页查询
+     * @param page IPage<SysSuggestionType>
+     * @param query SysSuggestionTypeWrapper.SysSuggestionTypeQuery
+     * @return IPage<SysSuggestionType>
+     */
+    @Override
+    public IPage<SysSuggestionType> selectPage(IPage<SysSuggestionType> page, SysSuggestionTypeWrapper.SysSuggestionTypeQuery query) {
+
+        return page.setRecords(baseMapper.selectPage(page, query));
+    }
+
+    /**
+     * 添加
+     * @param sysSuggestionType SysSuggestionTypeWrapper.SysSuggestionType
+     * @return Boolean
+     */
+    @Override
+    public Boolean add(SysSuggestionTypeWrapper.SysSuggestionType sysSuggestionType) {
+
+        return this.save(JSON.parseObject(sysSuggestionType.jsonString(), SysSuggestionType.class));
+    }
+
+    /**
+     * 更新
+     * @param sysSuggestionType SysSuggestionTypeWrapper.SysSuggestionType
+     * @return Boolean
+     */
+    @Override
+    public Boolean update(SysSuggestionTypeWrapper.SysSuggestionType sysSuggestionType){
+
+        return this.updateById(JSON.parseObject(sysSuggestionType.jsonString(), SysSuggestionType.class));
+    }
+}

+ 124 - 13
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/TenantStaffServiceImpl.java

@@ -1,14 +1,32 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
 import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.entity.TenantInfo;
 import com.yonge.cooleshow.biz.dal.entity.TenantStaff;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
+import com.yonge.cooleshow.biz.dal.mapper.TenantInfoMapper;
 import com.yonge.cooleshow.biz.dal.mapper.TenantStaffMapper;
 import com.yonge.cooleshow.biz.dal.service.TenantStaffService;
 import com.yonge.cooleshow.biz.dal.wrapper.TenantStaffWrapper;
+import com.yonge.cooleshow.common.enums.UserLockFlag;
+import com.yonge.toolset.base.exception.BizException;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 机构员工表
@@ -18,49 +36,124 @@ import org.springframework.stereotype.Service;
 @Service
 public class TenantStaffServiceImpl extends ServiceImpl<TenantStaffMapper, TenantStaff> implements TenantStaffService {
 
-	/**
+
+    @Autowired
+    private TenantInfoMapper tenantInfoMapper;
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Autowired
+    private SysUserFeignService sysUserFeignService;
+
+    /**
      * 查询详情
+     *
      * @param id 详情ID
      * @return TenantStaff
      */
-	@Override
+    @Override
     public TenantStaff detail(Long id) {
-        
+
         return baseMapper.selectById(id);
     }
-    
+
     /**
      * 分页查询
-     * @param page IPage<TenantStaff>
+     *
+     * @param page  IPage<TenantStaff>
      * @param query TenantStaffWrapper.TenantStaffQuery
      * @return IPage<TenantStaff>
      */
     @Override
-    public IPage<TenantStaff> selectPage(IPage<TenantStaff> page, TenantStaffWrapper.TenantStaffQuery query) {
-        
-        return page.setRecords(baseMapper.selectPage(page, query));
+    public IPage<TenantStaffWrapper.TenantStaff> selectPage(IPage<TenantStaffWrapper.TenantStaff> page, TenantStaffWrapper.TenantStaffQuery query) {
+        IPage<TenantStaffWrapper.TenantStaff> pageResult = page.setRecords(baseMapper.selectPage(page, query));
+        List<TenantStaffWrapper.TenantStaff> records = pageResult.getRecords();
+        if (CollectionUtils.isNotEmpty(records)) {
+            List<Long> tenantIdList = records.stream().map(TenantStaffWrapper.TenantStaff::getTenantId).distinct().collect(Collectors.toList());
+            QueryWrapper<TenantInfo> queryWrapper = new QueryWrapper<>();
+            queryWrapper.lambda().in(TenantInfo::getId, tenantIdList);
+            List<TenantInfo> tenantInfos = tenantInfoMapper.selectList(queryWrapper);
+            Map<Long, String> idNameMap = tenantInfos.stream().collect(Collectors.toMap(TenantInfo::getId, TenantInfo::getName));
+            records.forEach(next -> next.setTenantName(idNameMap.getOrDefault(next.getTenantId(), "")));
+        }
+        return pageResult;
     }
-	
+
     /**
      * 添加
+     *
      * @param tenantStaff TenantStaffWrapper.TenantStaff
      * @return Boolean
      */
     @Override
-    public Boolean add(TenantStaffWrapper.TenantStaff tenantStaff) {    	
-        
+    public Boolean add(TenantStaffWrapper.TenantStaff tenantStaff) {
+
         return this.save(JSON.parseObject(tenantStaff.jsonString(), TenantStaff.class));
     }
 
     /**
      * 更新
+     *
      * @param tenantStaff TenantStaffWrapper.TenantStaff
      * @return Boolean
      */
+    @Transactional(rollbackFor = Exception.class)
     @Override
-    public Boolean update(TenantStaffWrapper.TenantStaff tenantStaff){
+    public Boolean update(TenantStaffWrapper.TenantStaffAdd tenantStaff) {
+        TenantStaff staff = this.getById(tenantStaff.getId());
+        if (staff == null) {
+            throw new BizException("机构人员不存在");
+        }
+        staff.setNickname(tenantStaff.getNickname());
+        // 主管理员不能修改身份
+        if (Boolean.FALSE.equals(staff.getManageAdmin()) && Boolean.FALSE.equals(tenantStaff.getManageAdmin())) {
+            staff.setManageAdmin(tenantStaff.getManageAdmin());
+            // 更换了机构,清理wx关联ID
+            if (!staff.getTenantId().equals(tenantStaff.getTenantId())) {
+                staff.setTenantId(tenantStaff.getTenantId());
+                this.lambdaUpdate()
+                        .set(TenantStaff::getWxOpenid, null)
+                        .eq(TenantStaff::getId, staff.getId())
+                        .update();
+            }
+        }
+        Long userId = staff.getUserId();
+        com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = new com.yonge.cooleshow.biz.dal.entity.SysUser();
+        sysUser.setId(userId);
+        sysUser.setUsername(tenantStaff.getNickname());
+        sysUser.setGender(tenantStaff.getGender());
 
-        return this.updateById(JSON.parseObject(tenantStaff.jsonString(), TenantStaff.class));       
+        String phone = tenantStaff.getPhone();
+        // 修改手机号,处理账号
+        com.yonge.cooleshow.biz.dal.entity.SysUser oldUser = sysUserMapper.selectById(userId);
+        if (!oldUser.getPhone().equals(phone)) {
+            SysUser userByPhone = sysUserMapper.findUserByPhone(phone);
+            if (userByPhone != null) {
+                throw new BizException("该手机号已经注册账号");
+            } else {
+                sysUser.setPhone(phone);
+            }
+            String wxOpenid = staff.getWxOpenid();
+            // 存在值,说明登录并绑定过,执行退出登录
+            if (StringUtils.isNotEmpty(wxOpenid)) {
+                // 旧手机号退出登录
+                sysUserFeignService.exitByPhoneAndOpenId(ClientEnum.ORGANIZATION.getCode().toLowerCase(),
+                        sysUser.getPhone(), wxOpenid);
+                this.lambdaUpdate()
+                        .set(TenantStaff::getWxOpenid, null)
+                        .eq(TenantStaff::getId, staff.getId())
+                        .update();
+            }
+        }
+        sysUserMapper.updateById(sysUser);
+
+        tenantInfoMapper.update(null, Wrappers.<TenantInfo>lambdaUpdate()
+                .set(TenantInfo::getPhone, tenantStaff.getPhone())
+                .set(TenantInfo::getUsername, tenantStaff.getNickname())
+                .eq(TenantInfo::getUserId, staff.getUserId()));
+
+        return this.updateById(staff);
     }
 
     @Override
@@ -80,4 +173,22 @@ public class TenantStaffServiceImpl extends ServiceImpl<TenantStaffMapper, Tenan
 
         return lambdaQuery().eq(TenantStaff::getUserId, userId).last("LIMIT 1").one();
     }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void updateStatus(Long id, UserLockFlag status) {
+        this.lambdaUpdate()
+                .set(TenantStaff::getStatus, status)
+                .eq(TenantStaff::getId, id)
+                .update();
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void clearWxOpenId(Long id) {
+        this.lambdaUpdate()
+                .set(TenantStaff::getWxOpenid, null)
+                .eq(TenantStaff::getId, id)
+                .update();
+    }
 }

+ 107 - 29
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/UserTenantAlbumRecordServiceImpl.java

@@ -3,6 +3,7 @@ package com.yonge.cooleshow.biz.dal.service.impl;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
@@ -26,12 +27,14 @@ import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext;
 import com.yonge.toolset.utils.obj.ObjectUtil;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.beans.BeanUtils;
 import lombok.extern.slf4j.Slf4j;
 import com.yonge.cooleshow.biz.dal.wrapper.UserTenantAlbumRecordWrapper;
 import com.yonge.cooleshow.biz.dal.mapper.UserTenantAlbumRecordMapper;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.util.*;
@@ -63,7 +66,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
     private MusicSheetService musicSheetService;
 
     @Autowired
-    private  StudentService studentService;
+    private StudentService studentService;
 
     @Autowired
     private TenantAlbumService tenantAlbumService;
@@ -92,12 +95,13 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
 
     /**
      * 查询详情
+     *
      * @param id 详情ID
      * @return UserTenantAlbumRecord
      */
     @Override
     public UserTenantAlbumRecord detail(Long id) {
-        
+
         return baseMapper.selectById(id);
     }
 
@@ -107,7 +111,8 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
 
     /**
      * 分页查询
-     * @param page IPage<UserTenantAlbumRecord>
+     *
+     * @param page  IPage<UserTenantAlbumRecord>
      * @param query UserTenantAlbumRecordWrapper.UserTenantAlbumRecordQuery
      * @return IPage<UserTenantAlbumRecord>
      */
@@ -119,15 +124,15 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
 
         List<TenantAlbumWrapper.TenantAlbum> list = new ArrayList<>();
 
-            //查询生效的机构专辑id
-        if (id != null){
+        //查询生效的机构专辑id
+        if (id != null) {
             List<Long> tenantAlbumIds = baseMapper.selectTenantIds(id);
-            if (CollectionUtils.isEmpty(tenantAlbumIds)){
+            if (CollectionUtils.isEmpty(tenantAlbumIds)) {
                 return null;
             }
             List<TenantAlbum> tenantAlbums = baseMapper.selectTenantAlbumInfo(tenantAlbumIds);
 
-            tenantAlbums.stream().forEach(i->{
+            tenantAlbums.stream().forEach(i -> {
                 TenantAlbumWrapper.TenantAlbum vo = JSON.parseObject(JSON.toJSONString(i),
                         TenantAlbumWrapper.TenantAlbum.class);
 
@@ -192,26 +197,100 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
         return page.setRecords(list);
     }
 
+
+    /**
+     * 分页查询
+     *
+     * @param page  IPage<UserTenantAlbumRecord>
+     * @param query UserTenantAlbumRecordWrapper.UserTenantAlbumRecordQuery
+     * @return IPage<UserTenantAlbumRecord>
+     */
+    @Override
+    public IPage<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord> selectUserTenantAlbumRecordPage(IPage<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord> page,
+                                                                                                     UserTenantAlbumRecordWrapper.UserTenantAlbumRecordQuery query) {
+
+        IPage<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord> records = page.setRecords(baseMapper.selectUserTenantAlbumRecordPage(page, query));
+        List<UserTenantAlbumRecordWrapper.UserTenantAlbumRecord> rows = records.getRecords();
+        if (CollectionUtils.isNotEmpty(rows)) {
+            List<Long> collect = rows.stream().map(UserTenantAlbumRecordWrapper.UserTenantAlbumRecord::getTenantAlbumId).distinct().collect(Collectors.toList());
+            Map<Long, String> idNameMap = tenantAlbumService.lambdaQuery()
+                    .in(TenantAlbum::getId, collect)
+                    .list().stream().collect(Collectors.toMap(TenantAlbum::getId, TenantAlbum::getName));
+            rows.forEach(next -> next.setTenantAlbumName(idNameMap.getOrDefault(next.getTenantAlbumId(), "")));
+        }
+        return records;
+    }
+
     /**
      * 添加
+     *
      * @param userTenantAlbumRecord UserTenantAlbumRecordWrapper.UserTenantAlbumRecord
      * @return Boolean
      */
+    @Transactional(rollbackFor = Exception.class)
     @Override
-    public Boolean add(UserTenantAlbumRecordWrapper.UserTenantAlbumRecord userTenantAlbumRecord) {
+    public Boolean add(UserTenantAlbumRecord userTenantAlbumRecord) {
+        Long tenantAlbumId = userTenantAlbumRecord.getTenantAlbumId();
+        TenantAlbum tenantAlbum = tenantAlbumMapper.selectById(tenantAlbumId);
+        if (tenantAlbum == null || Boolean.TRUE.equals(tenantAlbum.getDelFlag())) {
+            throw new BizException("专辑不存在");
+        }
+        if (Boolean.FALSE.equals(tenantAlbum.getStatus())) {
+            throw new BizException("专辑已经停用");
+        }
 
-        return this.save(JSON.parseObject(userTenantAlbumRecord.jsonString(), UserTenantAlbumRecord.class));
+        List<UserTenantAlbumRecord> userTenantAlbumRecords = this.getBaseMapper()
+                .selectList(Wrappers.<UserTenantAlbumRecord>lambdaQuery()
+                        .eq(UserTenantAlbumRecord::getTenantId, userTenantAlbumRecord.getTenantId())
+                        .eq(UserTenantAlbumRecord::getUserId, userTenantAlbumRecord.getUserId())
+                        .eq(UserTenantAlbumRecord::getTenantAlbumId, userTenantAlbumRecord.getTenantAlbumId())
+                        .eq(UserTenantAlbumRecord::getClientType, ClientEnum.STUDENT)
+                        .orderByDesc(UserTenantAlbumRecord::getEndTime));
+
+        Calendar instance = Calendar.getInstance();
+        instance.setTime(DateTime.now().withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0).toDate());
+        if (!userTenantAlbumRecords.isEmpty()) {
+            // 如果最后一次的时间的小于当前时间,则以当前时间为会员的开始时间
+            // 如果最后一次的时间的大于当前时间,则以最后一次的结束时间为记录的开始时间,相当会员续期
+            UserTenantAlbumRecord lastRecord = userTenantAlbumRecords.get(0);
+            Date lastEndTime = lastRecord.getEndTime();
+            if (lastEndTime.after(new Date())) {
+                instance.setTime(lastEndTime);
+            }
+        }
+        userTenantAlbumRecord.setStartTime(instance.getTime());
+
+        String type = userTenantAlbumRecord.getType();
+        if ("DAY".equals(type)) {
+            instance.add(Calendar.DAY_OF_MONTH, userTenantAlbumRecord.getTimes());
+        } else if ("MONTH".equals(type)) {
+            instance.add(Calendar.MONTH, userTenantAlbumRecord.getTimes());
+        } else if ("YEAR".equals(type)) {
+            instance.add(Calendar.YEAR, userTenantAlbumRecord.getTimes());
+        } else {
+            throw new BizException("不支持的周期类型");
+        }
+
+        instance.set(Calendar.HOUR_OF_DAY, 23);
+        instance.set(Calendar.MINUTE, 59);
+        instance.set(Calendar.SECOND, 59);
+        instance.set(Calendar.MILLISECOND, 0);
+        userTenantAlbumRecord.setEndTime(instance.getTime());
+        userTenantAlbumRecord.setCreateTime(new Date());
+        userTenantAlbumRecord.setUpdateTime(new Date());
+        return this.save(userTenantAlbumRecord);
     }
 
     /**
      * 更新
+     *
      * @param userTenantAlbumRecord UserTenantAlbumRecordWrapper.UserTenantAlbumRecord
      * @return Boolean
      */
     @Override
-    public Boolean update(UserTenantAlbumRecordWrapper.UserTenantAlbumRecord userTenantAlbumRecord){
+    public Boolean update(UserTenantAlbumRecordWrapper.UserTenantAlbumRecord userTenantAlbumRecord) {
 
-        return this.updateById(JSON.parseObject(userTenantAlbumRecord.jsonString(), UserTenantAlbumRecord.class));       
+        return this.updateById(JSON.parseObject(userTenantAlbumRecord.jsonString(), UserTenantAlbumRecord.class));
     }
 
 
@@ -230,6 +309,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
         }
         return recordList.get(0);
     }
+
     /**
      * 获取最新的购买记录
      *
@@ -246,14 +326,14 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
                 .eq(UserTenantAlbumRecord::getTenantId, tenantId)
                 .eq(UserTenantAlbumRecord::getUserId, userId)
                 .eq(UserTenantAlbumRecord::getClientType, clientType)
-                .in(CollectionUtils.isNotEmpty(tenantAlbumIds),UserTenantAlbumRecord::getTenantAlbumId,tenantAlbumIds)
+                .in(CollectionUtils.isNotEmpty(tenantAlbumIds), UserTenantAlbumRecord::getTenantAlbumId, tenantAlbumIds)
                 .ge(UserTenantAlbumRecord::getEndTime, new Date())
                 .orderByDesc(UserTenantAlbumRecord::getEndTime)
                 .list();
     }
 
     @Override
-    public List<Long> getUseAlbumIdsByUserId(Long userId,ClientEnum clientType) {
+    public List<Long> getUseAlbumIdsByUserId(Long userId, ClientEnum clientType) {
         if (userId == null) {
             return new ArrayList<>();
         }
@@ -281,7 +361,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
             throw new BizException("用户不存在");
         }
         Long tenantAlbumId;
-        if (StringUtils.isEmpty(albumId)){
+        if (StringUtils.isEmpty(albumId)) {
             Long id = sysUser.getId();
             List<Student> list = studentService.lambdaQuery().eq(Student::getUserId, id).list();
             if (CollectionUtils.isEmpty(list)) {
@@ -292,7 +372,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
             Long tenantId = student.getTenantId();
             //查询对应专辑id
             List<TenantAlbumMusic> tenantAlbumMusicList = tenantAlbumMusicService.lambdaQuery().eq(TenantAlbumMusic::getTenantId, tenantId)
-                    .eq(TenantAlbumMusic::getDelFlag,false).list();
+                    .eq(TenantAlbumMusic::getDelFlag, false).list();
             if (CollectionUtils.isEmpty(tenantAlbumMusicList)) {
                 return null;
             }
@@ -304,7 +384,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
         }
         //获取对应机构专辑状态
         TenantAlbum one = tenantAlbumService.lambdaQuery().eq(TenantAlbum::getId, tenantAlbumId).last("limit 1").one();
-        if (!ObjectUtil.isEmpty(one)){
+        if (!ObjectUtil.isEmpty(one)) {
             album.setStatus(one.getStatus());
         }
 
@@ -321,7 +401,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
                 List<Long> albIds = albumRefs.stream().map(TenantAlbumRef::getTenantAlbumId).distinct().collect(Collectors.toList());
                 QueryWrapper<TenantAlbum> query = new QueryWrapper<>();
                 query.lambda().in(TenantAlbum::getId, albIds)
-                        .eq(TenantAlbum::getStatus,true);
+                        .eq(TenantAlbum::getStatus, true);
                 Integer count = tenantAlbumMapper.selectCount(query);
                 if (count > 0) {
                     album.setTenantAlbumStatus(1);
@@ -341,12 +421,10 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
         }
 
 
-
-
         //查询是否已经购买专辑
-        Long buyTenantAlbumId = userTenantAlbumRecordMapper.ifBuy(tenantAlbumId,sysUser.getId());
+        Long buyTenantAlbumId = userTenantAlbumRecordMapper.ifBuy(tenantAlbumId, sysUser.getId());
 
-        if (buyTenantAlbumId != null){
+        if (buyTenantAlbumId != null) {
             album.setIfBuy(true);
         } else {
             album.setIfBuy(false);
@@ -355,7 +433,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
 
         //查询对应专辑的详情
         List<TenantAlbum> list = tenantAlbumService.lambdaQuery().eq(TenantAlbum::getId, tenantAlbumId).list();
-        if (CollectionUtils.isEmpty(list)){
+        if (CollectionUtils.isEmpty(list)) {
             throw new BizException("机构专辑不存在");
         }
         TenantAlbum tenantAlbum = list.get(0);
@@ -364,10 +442,10 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
         //机构专辑封面
         String coverImg = tenantAlbum.getCoverImg();
         //机构专辑曲目数
-        List<TenantAlbumMusic> tenantAlbumMusiclist = tenantAlbumMusicService.lambdaQuery().eq(TenantAlbumMusic::getTenantAlbumId, tenantAlbumId).eq(TenantAlbumMusic::getDelFlag,false).list();
+        List<TenantAlbumMusic> tenantAlbumMusiclist = tenantAlbumMusicService.lambdaQuery().eq(TenantAlbumMusic::getTenantAlbumId, tenantAlbumId).eq(TenantAlbumMusic::getDelFlag, false).list();
         List<Long> MusicSheetIds = tenantAlbumMusiclist.stream().map(TenantAlbumMusic::getMusicSheetId).distinct().collect(Collectors.toList());
         //计算符合条件的个数
-        if (CollectionUtils.isNotEmpty(MusicSheetIds)){
+        if (CollectionUtils.isNotEmpty(MusicSheetIds)) {
             size = musicSheetService.lambdaQuery().in(MusicSheet::getId, MusicSheetIds).eq(MusicSheet::getState, true)
                     .eq(MusicSheet::getDelFlag, false).count();
         }
@@ -376,7 +454,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
         //获取合奏曲目数量
         List<TenantAlbumMusic> ensembleLits = tenantAlbumMusicService.lambdaQuery().eq(TenantAlbumMusic::getSubjectType, "ENSEMBLE")
                 .eq(TenantAlbumMusic::getTenantAlbumId, tenantAlbumId)
-                .eq(TenantAlbumMusic::getDelFlag,false).list();
+                .eq(TenantAlbumMusic::getDelFlag, false).list();
         List<Long> ensembleMusicSheetIds = ensembleLits.stream().map(TenantAlbumMusic::getMusicSheetId).distinct().collect(Collectors.toList());
 
         album.setEnsembleCounts(ensembleMusicSheetIds.size());
@@ -384,7 +462,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
         //获取小曲目的曲目数量
         List<TenantAlbumMusic> musicLists = tenantAlbumMusicService.lambdaQuery().eq(TenantAlbumMusic::getSubjectType, "MUSIC")
                 .eq(TenantAlbumMusic::getTenantAlbumId, tenantAlbumId)
-                .eq(TenantAlbumMusic::getDelFlag,false).list();
+                .eq(TenantAlbumMusic::getDelFlag, false).list();
         List<Long> musicSheetIds = musicLists.stream().map(TenantAlbumMusic::getMusicSheetId).distinct().collect(Collectors.toList());
 
         album.setMusicCounts(musicSheetIds.size());
@@ -392,7 +470,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
         //获取声部的曲目数量
         List<TenantAlbumMusic> subjectLists = tenantAlbumMusicService.lambdaQuery().eq(TenantAlbumMusic::getSubjectType, "SUBJECT")
                 .eq(TenantAlbumMusic::getTenantAlbumId, tenantAlbumId)
-                .eq(TenantAlbumMusic::getDelFlag,false).list();
+                .eq(TenantAlbumMusic::getDelFlag, false).list();
         List<Long> subjectSheetIds = subjectLists.stream().map(TenantAlbumMusic::getMusicSheetId).distinct().collect(Collectors.toList());
 
         album.setSubjectCounts(subjectSheetIds.size());
@@ -459,7 +537,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
     }
 
 
-    private void temporarySend(Long userId,Long tenantAlbumId) {
+    private void temporarySend(Long userId, Long tenantAlbumId) {
         SysUser sysUser = sysUserFeignService.queryUserById(userId);
         if (null == sysUser) {
             return;
@@ -473,7 +551,7 @@ public class UserTenantAlbumRecordServiceImpl extends ServiceImpl<UserTenantAlbu
 
         try {
             sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.TENANT_ALBUM_EXPIRE,
-                    receivers, null, 0, null, ClientEnum.TENANT_STUDENT.getCode(),tenantAlbum.getName());
+                    receivers, null, 0, null, ClientEnum.TENANT_STUDENT.getCode(), tenantAlbum.getName());
         } catch (Exception e) {
             log.error("机构学生训练教材过期", e);
         }

+ 89 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/SysSuggestionTypeWrapper.java

@@ -0,0 +1,89 @@
+package com.yonge.cooleshow.biz.dal.wrapper;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Date;
+import java.util.Optional;
+
+/**
+ * 平台建议类型表
+ * 2023-08-30 11:26:33
+ */
+@ApiModel(value = "SysSuggestionTypeWrapper对象", description = "平台建议类型表查询对象")
+public class SysSuggestionTypeWrapper {
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(" SysSuggestionTypeQuery-平台建议类型表")
+    public static class SysSuggestionTypeQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        @ApiModelProperty("关键字匹配")
+        private String keyword;
+
+        public String getKeyword() {
+            return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
+        }
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static SysSuggestionTypeQuery from(String json) {
+            return JSON.parseObject(json, SysSuggestionTypeQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" SysSuggestionType-平台建议类型表")
+    public static class SysSuggestionType {
+
+        @ApiModelProperty("编号")
+        private Long id;
+
+        @ApiModelProperty("名称")
+        private String name;
+
+        @ApiModelProperty("删除标识(0-正常,1-删除)")
+        private Boolean delFlag;
+
+        @ApiModelProperty("创建人")
+        private Long createBy;
+
+        @ApiModelProperty("修改人")
+        private Long updateBy;
+
+        @ApiModelProperty("修改人名称")
+        private String operator;
+
+        @ApiModelProperty("更新时间")
+        private Date updateTime;
+
+        @ApiModelProperty("创建时间")
+        private Date createTime;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static SysSuggestionType from(String json) {
+            return JSON.parseObject(json, SysSuggestionType.class);
+        }
+    }
+
+}

+ 161 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/SysSuggestionWrapper.java

@@ -0,0 +1,161 @@
+package com.yonge.cooleshow.biz.dal.wrapper;
+
+import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.yonge.cooleshow.biz.dal.enums.SuggestionType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+import java.util.Optional;
+
+/**
+ * 平台建议表
+ * 2023-12-20 11:27:10
+ */
+@ApiModel(value = "SysSuggestionWrapper对象", description = "平台建议表查询对象")
+public class SysSuggestionWrapper {
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel(" SysSuggestionQuery-平台建议表")
+    public static class SysSuggestionQuery implements QueryInfo {
+
+        @ApiModelProperty("当前页")
+        private Integer page;
+
+        @ApiModelProperty("分页行数")
+        private Integer rows;
+
+        @ApiModelProperty("关键字匹配")
+        private String keyword;
+
+        @ApiModelProperty(value = "建议类型")
+        private SuggestionType type;
+
+        private String startTime;
+
+        private String endTime;
+
+        @ApiModelProperty(value = "客户端类型")
+        private String clientType;
+
+        public String getKeyword() {
+            return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
+        }
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static SysSuggestionQuery from(String json) {
+            return JSON.parseObject(json, SysSuggestionQuery.class);
+        }
+    }
+
+    @Data
+    @ApiModel(" SysSuggestion-平台建议表")
+    public static class SysSuggestion {
+
+        @ApiModelProperty("编号")
+        private Long id;
+
+        @ApiModelProperty("联系方式")
+        private String mobileNo;
+
+        @ApiModelProperty("标题")
+        private String title;
+
+        @ApiModelProperty("内容")
+        private String content;
+
+        @ApiModelProperty("链接")
+        private String url;
+
+        @ApiModelProperty("用户编号")
+        private Long userId;
+
+        @ApiModelProperty("提交时间")
+        private Date createTime;
+
+        @ApiModelProperty("客户端类型")
+        private String clientId;
+
+        @ApiModelProperty("建议类型")
+        private SuggestionType type;
+
+        @ApiModelProperty("客户端类型")
+        private String clientType;
+
+        @ApiModelProperty("建议类型ID")
+        private Long suggestionTypeId;
+
+        @ApiModelProperty("客户端信息")
+        private String userAgent;
+
+        @ApiModelProperty("处理状态:true:已处理,false:待处理")
+        private Boolean handleStatus;
+
+        @ApiModelProperty("处理时间")
+        private Date handleTime;
+
+        @ApiModelProperty("处理人")
+        private Long handleBy;
+
+        @ApiModelProperty("是否反馈用户,true:是,false:否")
+        private Boolean feedback;
+
+        @ApiModelProperty("反馈内容")
+        private String feedbackContent;
+
+//        @ApiModelProperty(value = "用户名")
+//        private String username;
+
+        @ApiModelProperty(value = "处理人姓名")
+        private String handleName;
+
+        @ApiModelProperty("建议类型名称")
+        private String suggestionTypeName;
+
+        @ApiModelProperty("用户名")
+        private String nickname;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static SysSuggestion from(String json) {
+            return JSON.parseObject(json, SysSuggestion.class);
+        }
+    }
+
+    @ApiModel("处理反馈意见模型")
+    @Data
+    public static class HandleSuggestion {
+
+        @ApiModelProperty("id")
+        @NotNull
+        private Long id;
+
+        @ApiModelProperty("是否反馈用户,true:是,false:否")
+        @NotNull
+        private Boolean feedback;
+
+        @ApiModelProperty("反馈内容")
+        @NotNull
+        private String feedbackContent;
+
+        @ApiModelProperty(value = "处理人", hidden = true)
+        private Long handleBy;
+
+    }
+
+}

+ 108 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/TenantStaffWrapper.java

@@ -1,9 +1,15 @@
 package com.yonge.cooleshow.biz.dal.wrapper;
 
 import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.microsvc.toolkit.common.response.paging.QueryInfo;
+import com.yonge.cooleshow.common.enums.UserLockFlag;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Date;
 import java.util.Optional;
 
 import lombok.AllArgsConstructor;
@@ -34,7 +40,19 @@ public class TenantStaffWrapper {
         
         @ApiModelProperty("关键字匹配")
 		private String keyword;
-        
+
+        @ApiModelProperty("机构ID")
+        private Integer tenantId;
+
+        @ApiModelProperty("账号状态,0:正常,1:锁定")
+        private UserLockFlag status;
+
+        @ApiModelProperty("是否是主管理员")
+        private Boolean manageAdmin;
+
+        @ApiModelProperty("绑定状态")
+        private Boolean bindStatus;
+
         public String getKeyword() {
             return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
         }
@@ -48,9 +66,64 @@ public class TenantStaffWrapper {
         }
     }  
 
+    @Data
 	@ApiModel(" TenantStaff-机构员工表")
     public static class TenantStaff {
-        
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+        @ApiModelProperty("机构ID")
+        private Long tenantId;
+
+        @ApiModelProperty("用户ID")
+        private Long userId;
+
+        @ApiModelProperty("头像")
+        private String avatar;
+
+        @ApiModelProperty("昵称")
+        private String nickname;
+
+        @ApiModelProperty("IM授权签名")
+        private String imToken;
+
+        @ApiModelProperty("微信ID")
+        private String wechatId;
+
+        @ApiModelProperty("微信OpenID")
+        private String wxOpenid;
+
+        @ApiModelProperty("公众号关注标识")
+        private Boolean subscribeFlag;
+
+        @ApiModelProperty("管理员标识")
+        private Boolean manageAdmin;
+
+        @ApiModelProperty("介绍")
+        private String introduction;
+
+        @ApiModelProperty("帐号状态(注销,冻结,激活)")
+        private UserLockFlag status;
+
+        @ApiModelProperty("更新时间")
+        private Date updateTime;
+
+        @ApiModelProperty("创建时间")
+        private Date createTime;
+
+        @ApiModelProperty("机构名称")
+        private String tenantName;
+
+        @ApiModelProperty("手机号")
+        private String phone;
+
+        @ApiModelProperty("绑定状态")
+        private Boolean bindStatus;
+
+        @ApiModelProperty("性别,0:女,1:男")
+        private Integer gender;
+
         public String jsonString() {
             return JSON.toJSONString(this);
         }
@@ -60,4 +133,37 @@ public class TenantStaffWrapper {
         }
 	}
 
+    @Data
+    @ApiModel(" TenantStaff-机构员工表")
+    public static class TenantStaffAdd {
+
+        @ApiModelProperty("主键ID")
+        private Long id;
+
+        @ApiModelProperty("机构ID")
+        private Long tenantId;
+
+        @ApiModelProperty("用户ID")
+        private Long userId;
+
+        @ApiModelProperty("昵称")
+        private String nickname;
+
+        @ApiModelProperty("性别,0:女,1:男")
+        private Integer gender;
+
+        @ApiModelProperty("手机号")
+        private String phone;
+
+        @ApiModelProperty("是否是主管理员")
+        private Boolean manageAdmin;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+
+        public static TenantStaff from(String json) {
+            return JSON.parseObject(json, TenantStaff.class);
+        }
+    }
 }

+ 28 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/UserTenantAlbumRecordWrapper.java

@@ -45,6 +45,15 @@ public class UserTenantAlbumRecordWrapper {
         @ApiModelProperty("用户Id")
         private Long userId;
 
+        @ApiModelProperty("开始时间")
+        private Date startTime;
+
+        @ApiModelProperty("结束时间")
+        private Date endTime;
+
+        @ApiModelProperty("ORDER:激活码激活,TENANT:自行购买,BACKEND_GIVE:后台添加")
+        private SourceTypeEnum sourceType;
+
         
         public String getKeyword() {
             return Optional.ofNullable(keyword).filter(StringUtils::isNotBlank).orElse(null);
@@ -59,6 +68,9 @@ public class UserTenantAlbumRecordWrapper {
         }
     }  
 
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
     @Data
 	@ApiModel(" UserTenantAlbumRecord-购买训练工具记录")
     public static class UserTenantAlbumRecord {
@@ -78,10 +90,13 @@ public class UserTenantAlbumRecordWrapper {
         @ApiModelProperty("机构专辑ID")
         private Long tenantAlbumId;
 
+        @ApiModelProperty("机构专辑ID")
+        private String tenantAlbumName;
+
         @ApiModelProperty("订单号")
         private String orderNo;
 
-        @ApiModelProperty("ORDER:订单")
+        @ApiModelProperty("ORDER:激活码激活,TENANT:自行购买,BACKEND_GIVE:后台添加")
         private SourceTypeEnum sourceType;
 
         @ApiModelProperty("购买人员类型 TEACHRE :老师端 STUDNET : 学生端")
@@ -108,6 +123,18 @@ public class UserTenantAlbumRecordWrapper {
         @ApiModelProperty("备注")
         private String reason;
 
+        @ApiModelProperty("创建时间")
+        private Date createTime;
+
+        @ApiModelProperty("创建人")
+        private Long createBy;
+
+        @ApiModelProperty("创建人")
+        private String createName;
+
+        @ApiModelProperty("更新时间")
+        private Date updateTime;
+
         public String jsonString() {
             return JSON.toJSONString(this);
         }

+ 62 - 92
cooleshow-user/user-biz/src/main/resources/config/mybatis/SysSuggestionMapper.xml

@@ -1,112 +1,82 @@
 <?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="com.yonge.cooleshow.biz.dal.dao.SysSuggestionDao">
+<!DOCTYPE  mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.yonge.cooleshow.biz.dal.mapper.SysSuggestionMapper">
 
-    <resultMap type="com.yonge.cooleshow.biz.dal.entity.SysSuggestion" id="SysSuggestion">
-        <result column="id_" property="id"/>
-        <result column="mobile_no_" property="mobileNo"/>
-        <result column="title_" property="title"/>
-        <result column="content_" property="content"/>
-        <result column="url_" property="url"/>
-        <result column="user_id_" property="userId"/>
-        <result column="username_" property="username"/>
-        <result column="create_time_" property="createTime"/>
-        <result column="client_type_" property="clientType"/>
-        <result column="type_" property="type" typeHandler="com.yonge.toolset.mybatis.dal.CustomEnumTypeHandler"/>
-    </resultMap>
 
-    <!-- 根据主键查询一条记录 -->
-    <select id="get" resultMap="SysSuggestion">
-		SELECT * FROM sys_suggestion WHERE id_ = #{id}
-	</select>
 
-    <!-- 全查询 -->
-    <select id="findAll" resultMap="SysSuggestion">
-		SELECT * FROM sys_suggestion ORDER BY id_
-	</select>
-
-    <!-- 向数据库增加一条记录 -->
-    <insert id="insert" parameterType="com.yonge.cooleshow.biz.dal.entity.SysSuggestion" useGeneratedKeys="true" keyColumn="id"
-            keyProperty="id">
-        INSERT INTO sys_suggestion (mobile_no_,title_,content_,user_id_,create_time_,client_type_,type_,url_)
-        VALUES(#{mobileNo},#{title},#{content},#{userId},now(),#{clientType},#{type, typeHandler=com.yonge.toolset.mybatis.dal.CustomEnumTypeHandler},#{url})
-    </insert>
+    <!-- 表字段 -->
+    <sql id="baseColumns">
+        t.id_ AS id
+        , t.mobile_no_ AS mobileNo
+        , t.title_ AS title
+        , t.content_ AS content
+        , t.url_ AS url
+        , t.user_id_ AS userId
+        , t.create_time_ AS createTime
+        , t.client_id_ AS clientId
+        , t.type_ AS type
+        , t.client_type_ AS clientType
+        , t.suggestion_type_id_ AS suggestionTypeId
+        , t.user_agent_ AS userAgent
+        , t.handle_status_ AS handleStatus
+        , t.handle_time_ AS handleTime
+        , t.handle_by_ AS handleBy
+        , t.feedback_ AS feedback
+        , t.feedback_content_ AS feedbackContent
+    </sql>
 
-    <!-- 根据主键查询一条记录 -->
-    <update id="update" parameterType="com.yonge.cooleshow.biz.dal.entity.SysSuggestion">
-        UPDATE sys_suggestion
-        <set>
-            <if test="url != null">
-                url_ = #{url},
+    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.wrapper.SysSuggestionWrapper$SysSuggestion">
+        SELECT
+        <include refid="baseColumns" />
+        ,CASE WHEN (t.client_type_='STUDENT' or t.client_type_ = 'TENANT_STUDENT') THEN su.username_ ELSE su.real_name_ END nickname
+        ,sst.name_ as suggestionTypeName
+        FROM sys_suggestion t
+        LEFT JOIN sys_user su ON su.id_ = t.user_id_
+        LEFT JOIN sys_suggestion_type sst on t.suggestion_type_id_ = sst.id_
+        <where>
+            su.del_flag_ = 0
+            <if test="param.keyword!=null and param.keyword.trim()!=''">
+                AND (
+                su.real_name_ LIKE CONCAT('%', #{param.keyword}, '%')
+                OR su.username_ LIKE CONCAT('%', #{search}, '%')
+<!--                OR t.user_id_=#{search} OR su.phone_=#{search}-->
+                )
             </if>
-            <if test="clientType != null">
-                client_type_ = #{clientType},
+            <if test="param.type != null">
+                AND t.type_ = #{param.type}
             </if>
-            <if test="userId != null">
-                user_id_ = #{userId},
+            <if test="param.clientType != null">
+                AND t.client_type_ = #{param.clientType}
             </if>
-            <if test="title != null">
-                title_ = #{title},
+            <if test="param.startTime!=null">
+                AND t.create_time_ >= #{param.startTime}
             </if>
-            <if test="content != null">
-                content_ = #{content},
+            <if test="param.endTime!=null">
+                AND #{param.endTime} >= t.create_time_
             </if>
-            <if test="mobileNo != null">
-                mobile_no_ = #{mobileNo},
+            <if test="param.suggestionTypeId != null">
+                AND t.suggestion_type_id_ = #{param.suggestionTypeId}
             </if>
-            <if test="type != null">
-                type_ = #{type, typeHandler=com.yonge.toolset.mybatis.dal.CustomEnumTypeHandler},
+            <if test="param.suggestionTypeName != null and param.suggestionTypeName.trim() != ''">
+                AND sst.name_ = #{param.suggestionTypeName}
             </if>
-        </set>
-        WHERE id_ = #{id} 
-    </update>
-
-    <!-- 根据主键删除一条记录 -->
-    <delete id="delete">
-		DELETE FROM sys_suggestion WHERE id_ = #{id} 
-	</delete>
-
-    <sql id="queryCondition">
-        <where>
-            su.del_flag_ = 0
-            <if test="type!=null">
-                AND ss.type_ = #{type, typeHandler=com.yonge.toolset.mybatis.dal.CustomEnumTypeHandler}
+            <if test="param.handleStatus != null">
+                AND t.handle_status_ = #{param.handleStatus}
             </if>
-            <if test="clientType!=null">
-                AND ss.client_type_ = #{clientType}
+            <if test="param.handleBy != null">
+                AND t.handle_by_ = #{param.handleBy}
             </if>
-            <if test="startTime!=null and startTime!=''">
-                AND DATE_FORMAT(ss.create_time_,'%Y-%m-%d') &gt;= #{startTime}
+            <if test="param.handStartTime != null">
+                AND t.handle_time_ >= #{param.handStartTime}
             </if>
-            <if test="endTime!=null and endTime!=''">
-                AND DATE_FORMAT(ss.create_time_,'%Y-%m-%d') &lt;= #{endTime}
+            <if test="param.handEndTime != null">
+                AND #{param.handEndTime} >= t.handle_time_
             </if>
-            <if test="search!=null and search!=''">
-                AND (su.real_name_ LIKE CONCAT('%', #{search}, '%') OR su.username_ LIKE CONCAT('%', #{search}, '%') OR ss.user_id_=#{search} OR su.phone_=#{search})
+            <if test="param.feedback != null">
+                AND t.feedback_ = #{param.feedback}
             </if>
         </where>
-    </sql>
-
-    <!-- 分页查询 -->
-    <select id="queryPage" resultMap="SysSuggestion" parameterType="map">
-        SELECT ss.*,
-        CASE WHEN (ss.client_type_='STUDENT' or ss.client_type_ = 'TENANT_STUDENT') THEN su.username_ ELSE su.real_name_ END
-        username_
-        FROM sys_suggestion ss
-        LEFT JOIN sys_user su ON su.id_ = ss.user_id_
-        <include refid="queryCondition" />
-        ORDER BY ss.id_ DESC
-        <include refid="global.limit"/>
+        ORDER BY t.id_ DESC
     </select>
 
-    <!-- 查询当前表的总记录数 -->
-    <select id="queryCount" resultType="int">
-		SELECT COUNT(ss.id_) FROM sys_suggestion ss
-        LEFT JOIN sys_user su ON su.id_ = ss.user_id_
-        <include refid="queryCondition" />
-	</select>
-</mapper>
+</mapper>

+ 26 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/SysSuggestionTypeMapper.xml

@@ -0,0 +1,26 @@
+<?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="com.yonge.cooleshow.biz.dal.mapper.SysSuggestionTypeMapper">
+
+
+
+    <!-- 表字段 -->
+    <sql id="baseColumns">
+        t.id_ AS id
+        , t.name_ AS `name`
+        , t.del_flag_ AS delFlag
+        , t.create_by_ AS createBy
+        , t.update_by_ AS updateBy
+        , t.update_time_ AS updateTime
+        , t.create_time_ AS createTime
+    </sql>
+
+    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.entity.SysSuggestionType">
+        SELECT
+        <include refid="baseColumns"/>
+        FROM sys_suggestion_type t
+        where del_flag_ = 0
+        order by t.create_time_ desc
+    </select>
+
+</mapper>

+ 34 - 5
cooleshow-user/user-biz/src/main/resources/config/mybatis/TenantStaffMapper.xml

@@ -27,13 +27,42 @@
 
     </update>
 
-    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.entity.TenantStaff">
-		SELECT         
-        	<include refid="baseColumns" />
+    <select id="selectPage" resultType="com.yonge.cooleshow.biz.dal.wrapper.TenantStaffWrapper$TenantStaff">
+        SELECT
+        <include refid="baseColumns"/>
         ,su.avatar_ as avatar
-		FROM tenant_staff t
+        ,su.phone_ as phone
+        ,if(t.wx_openid_ is null,false,true) bindStatus
+        FROM tenant_staff t
         left join sys_user su on t.user_id_ = su.id_
-	</select>
+        <where>
+            <if test="param.keyword != null and param.keyword.trim() != ''">
+                and (
+                t.user_id_ like concat('%',#{param.keyword},'%')
+                or t.nickname_ like concat('%',#{param.keyword},'%')
+                or su.phone_ like concat('%',#{param.keyword},'%')
+                )
+            </if>
+            <if test="param.tenantId != null">
+                and t.tenant_id_ = #{param.tenantId}
+            </if>
+            <if test="param.status != null">
+                and t.status_ = #{param.status}
+            </if>
+            <if test="param.manageAdmin != null">
+                and t.manage_admin_ = #{param.manageAdmin}
+            </if>
+            <if test="param.bindStatus != null">
+                <if test="param.bindStatus == 1">
+                    and t.wx_openid_ is not null
+                </if>
+                <if test="param.bindStatus == 0">
+                    and t.wx_openid_ is null
+                </if>
+            </if>
+        </where>
+        order by t.id_ desc
+    </select>
 
     <select id="getByPhone" resultType="com.yonge.cooleshow.biz.dal.entity.TenantStaff">
         select

+ 32 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/UserTenantAlbumRecordMapper.xml

@@ -88,4 +88,36 @@
         and (msg_status_ = 0 or msg_status_ is null)
         order by end_time_ desc
     </select>
+
+    <select id="selectUserTenantAlbumRecordPage" resultType="com.yonge.cooleshow.biz.dal.wrapper.UserTenantAlbumRecordWrapper$UserTenantAlbumRecord">
+        SELECT
+        <include refid="baseColumns" />
+        ,su.username_ createName
+        FROM user_tenant_album_record t
+        <if test="param.keyword != null and param.keyword.trim() != ''">
+            LEFT JOIN tenant_album ta on t.tenant_album_id_ = ta.id_
+        </if>
+        LEFT JOIN sys_user su on su.id_ = t.create_by_
+        <where>
+            <if test="param.keyword != null and param.keyword.trim() != ''">
+                AND (
+                su.username_ LIKE concat('%',#{param.keyword},'%')
+                OR ta.name_ LIKE concat('%',#{param.keyword},'%')
+                )
+            </if>
+            <if test="param.userId != null">
+                AND t.user_id_ = #{param.userId}
+            </if>
+            <if test="param.sourceType != null">
+                AND t.source_type_ = #{param.sourceType}
+            </if>
+            <if test="param.startTime != null">
+                AND t.create_time_ >= #{param.startTime}
+            </if>
+            <if test="param.endTime != null">
+                AND #{param.endTime} >= t.create_time_
+            </if>
+        </where>
+        ORDER BY t.id_ DESC
+    </select>
 </mapper>

+ 1 - 1
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/SysSuggestionController.java

@@ -48,7 +48,7 @@ public class SysSuggestionController extends BaseController {
             sysSuggestion.setClientType(ClientEnum.TENANT_STUDENT.getCode());
         }
 
-        sysSuggestionService.insert(sysSuggestion);
+        sysSuggestionService.save(sysSuggestion);
         return succeed();
     }