UsersService.java 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. package com.qxgmat.service;
  2. import com.github.pagehelper.Page;
  3. import com.nuliji.tools.AbstractService;
  4. import com.nuliji.tools.CipherHelp;
  5. import com.nuliji.tools.Tools;
  6. import com.nuliji.tools.Transform;
  7. import com.nuliji.tools.exception.AuthException;
  8. import com.nuliji.tools.exception.ParameterException;
  9. import com.nuliji.tools.exception.SystemException;
  10. import com.nuliji.tools.mybatis.Example;
  11. import com.nuliji.tools.mybatis.NativeJsonHandler;
  12. import com.nuliji.tools.third.OauthData;
  13. import com.qxgmat.data.constants.enums.status.DirectionStatus;
  14. import com.qxgmat.data.constants.enums.user.PrepareExaminationTime;
  15. import com.qxgmat.data.dao.UserMapper;
  16. import com.qxgmat.data.dao.entity.User;
  17. import com.qxgmat.data.dao.entity.UserOrder;
  18. import com.qxgmat.data.inline.UserToken;
  19. import com.qxgmat.data.relation.UserRelationMapper;
  20. import com.qxgmat.data.relation.entity.UserPrepareRelation;
  21. import com.qxgmat.help.WechatHelp;
  22. import com.qxgmat.service.extend.MessageExtendService;
  23. import com.qxgmat.service.extend.OrderFlowService;
  24. import com.qxgmat.service.inline.UserCourseService;
  25. import com.qxgmat.service.inline.UserMessageService;
  26. import com.qxgmat.service.inline.UserOrderService;
  27. import org.hibernate.criterion.Order;
  28. import org.springframework.beans.factory.annotation.Value;
  29. import org.springframework.stereotype.Service;
  30. import org.springframework.transaction.annotation.Transactional;
  31. import javax.annotation.Resource;
  32. import java.math.BigDecimal;
  33. import java.util.*;
  34. /**
  35. * Created by GaoJie on 2017/11/1.
  36. */
  37. @Service
  38. public class UsersService extends AbstractService {
  39. final public String PLATORM_WECHAT_NATIVE = "wechatOpenidWechat";
  40. final public String PLATORM_WECHAT_PC = "wechatOpenidPc";
  41. final public String PLATORM_WECHAT = "wechatUnionid";
  42. @Value("${self.secret}")
  43. private String secret;
  44. private NativeJsonHandler<UserToken> tokenHandler = new NativeJsonHandler<UserToken>(UserToken.class);
  45. @Resource
  46. private UserMapper userMapper;
  47. @Resource
  48. private WechatHelp wechatHelp;
  49. @Resource
  50. private UserRelationMapper userRelationMapper;
  51. @Resource
  52. private OrderFlowService orderFlowService;
  53. @Resource
  54. private MessageExtendService messageExtendService;
  55. /**
  56. * 生成有效期token
  57. * @param user
  58. * @return
  59. */
  60. public String getTokenByUser(User user){
  61. UserToken ut = new UserToken();
  62. ut.setId(user.getId());
  63. Date expire = new Date(new Date().getTime() + 86400);
  64. ut.setExpire(expire);
  65. String info = tokenHandler.toJson(ut);
  66. return CipherHelp.encrypt(info, CipherHelp.DES, secret);
  67. }
  68. /**
  69. * 解析有效期token
  70. * @param token
  71. * @return
  72. */
  73. public User getUserByToken(String token){
  74. // 默认测试token
  75. if (token.equals("1234567890")){
  76. return get(1);
  77. }else{
  78. String info = CipherHelp.decrypt(token, CipherHelp.DES, secret);
  79. UserToken ut = tokenHandler.toObject(info);
  80. Date expire = ut.getExpire();
  81. if (expire.before(new Date())){
  82. throw new AuthException("token错误");
  83. }
  84. return get(ut.getId());
  85. }
  86. }
  87. /**
  88. * 绑定第三方账号信息
  89. * @param user 当前登录用户
  90. * @param code
  91. * @param platform
  92. * @return
  93. */
  94. @Transactional
  95. public User Oauth(User user, String code, String platform, boolean userInfo){
  96. OauthData data;
  97. switch(platform){
  98. case "wechat_pc":
  99. data = wechatHelp.oauthPc(code, userInfo);
  100. break;
  101. case "wechat_native":
  102. data = wechatHelp.oauthNative(code, userInfo);
  103. break;
  104. default:
  105. throw new ParameterException("第三方平台"+platform+"不支持");
  106. }
  107. User openUser = getByOpen(data.getOpenId(), data.getUnionId(), platform);
  108. // 获取已关联的账号
  109. if (user != null && user.getId() != null){
  110. // 检验是否已经绑定
  111. if(openUser != null && !openUser.getId().equals(user.getId())){
  112. // // 自动合并账号
  113. // // 更新消息
  114. // userMessageService.mergeUser(openUser.getId(), user.getId());
  115. // // 更新消费记录
  116. // userOrderService.mergeUser(openUser.getId(), user.getId());
  117. // // 更新课程信息
  118. // userCourseService.mergeUser(openUser.getId(), user.getId());
  119. // // 更新服务信息
  120. // userServiceService.mergeUser(openUser.getId(), user.getId());
  121. //
  122. // // 更新实名认证
  123. // if (openUser.getRealStatus() > 0){
  124. // user.setRealAddress(openUser.getRealAddress());
  125. // user.setRealIdentity(openUser.getRealIdentity());
  126. // user.setRealName(openUser.getRealName());
  127. // user.setRealPhotoFront(openUser.getRealPhotoFront());
  128. // user.setRealPhotoBack(openUser.getRealPhotoBack());
  129. // user.setRealStatus(openUser.getRealStatus());
  130. // }
  131. //
  132. // // 删除旧账号
  133. // delete(openUser.getId());
  134. throw new ParameterException("该微信账户已绑定其他手机号,您可直接使用微信登录");
  135. }
  136. }
  137. User mm = User.builder()
  138. .id(openUser != null ? openUser.getId() : null)
  139. .build();
  140. // if (openUser ==null || openUser.getAvatar() == null || openUser.getAvatar().isEmpty()) {
  141. // mm.setAvatar(data.getAvatar());
  142. // if(openUser != null){
  143. // openUser.setAvatar(data.getAvatar());
  144. // }
  145. // }
  146. // if (openUser == null || openUser.getNickname() == null|| openUser.getNickname().isEmpty()){
  147. // mm.setNickname(data.getNickName());
  148. // if(openUser != null){
  149. // openUser.setNickname(data.getNickName());
  150. // }
  151. // }
  152. switch(platform){
  153. case "wechat_pc":
  154. mm.setWechatOpenidPc(data.getOpenId());
  155. mm.setWechatUnionid(data.getUnionId());
  156. break;
  157. case "wechat_native":
  158. mm.setWechatOpenidWechat(data.getOpenId());
  159. mm.setWechatUnionid(data.getUnionId());
  160. mm.setWechatAccessToken(data.getAccessToken());
  161. mm.setWechatRefreshToken(data.getRefreshToken());
  162. mm.setWechatExpireTime(data.getExpiresTime());
  163. break;
  164. }
  165. if (mm.getId() != null){
  166. // 直接更新数据
  167. edit(mm);
  168. return openUser;
  169. }
  170. return mm;
  171. }
  172. /**
  173. * 通过手机号注册
  174. * @param mobile
  175. * @param inviteCode
  176. * @param openUser
  177. * @return
  178. */
  179. @Transactional
  180. public User register(String area, String mobile, String inviteCode, String email, User openUser, String registerIp, String[] registerInfo){
  181. boolean n = false;
  182. User user = getByMobile(area, mobile);
  183. if (user != null){
  184. if (openUser == null) {
  185. // 直接手机登录,异常抛出,根据情况忽略
  186. throw new ParameterException("手机号已注册");
  187. }else if(user.getWechatUnionid() != null && !user.getWechatUnionid().equals("")){
  188. // openUser不为空,则用于绑定微信
  189. throw new ParameterException("该手机已绑定其他账号,请更换手机号码");
  190. }
  191. user = User.builder().id(user.getId()).build();
  192. if (user.getRegisterIp() == null || user.getRegisterIp().isEmpty()){
  193. user.setRegisterIp(registerIp);
  194. user.setRegisterCity(registerInfo != null ? String.join(",",registerInfo) : "");
  195. user.setLatestLoginIp(registerIp);
  196. }
  197. }else{
  198. // 注册,并且绑定邀请者
  199. user = User.builder().area(area).mobile(mobile).email(email).build();
  200. n = true;
  201. if (inviteCode != null && !inviteCode.isEmpty()){
  202. User origin = getByInviteCode(inviteCode);
  203. user.setOriginId(origin.getId());
  204. edit(User.builder().id(origin.getId()).inviteNumber(origin.getInviteNumber() + 1).inviteLatestTime(new Date()).build());
  205. // 邀请奖励
  206. orderFlowService.giveInvite(origin);
  207. }
  208. // 生成邀请码: 10位字符串
  209. user.setInviteCode(Tools.getRandomString(10));
  210. user.setRegisterIp(registerIp);
  211. user.setRegisterCity(registerInfo != null ? String.join(",",registerInfo) : "");
  212. user.setLatestLoginIp(registerIp);
  213. user.setNickname("qx"+user.getMobile());
  214. }
  215. // 绑定第三方登录信息
  216. if (openUser != null){
  217. this.bind(user, openUser);
  218. }
  219. if (n){
  220. user = add(user);
  221. messageExtendService.sendRegister(user);
  222. }else{
  223. user = edit(user);
  224. }
  225. if(user == null)
  226. throw new SystemException("注册失败");
  227. return user;
  228. }
  229. public User bind(User user, User openUser){
  230. // 绑定第三方登录信息
  231. if(openUser.getWechatOpenidPc() != null) user.setWechatOpenidPc(openUser.getWechatOpenidPc());
  232. if(openUser.getWechatOpenidWechat() != null) user.setWechatOpenidWechat(openUser.getWechatOpenidWechat());
  233. if(openUser.getWechatExpireTime() != null) user.setWechatExpireTime(openUser.getWechatExpireTime());
  234. if(openUser.getWechatUnionid() != null) user.setWechatUnionid(openUser.getWechatUnionid());
  235. if(openUser.getWechatAccessToken() != null) user.setWechatAccessToken(openUser.getWechatAccessToken());
  236. if(openUser.getWechatRefreshToken() != null) user.setWechatRefreshToken(openUser.getWechatRefreshToken());
  237. // if(openUser.getNickname() != null) user.setNickname(openUser.getNickname());
  238. // if(openUser.getAvatar() != null) user.setAvatar(openUser.getAvatar());
  239. return user;
  240. }
  241. // 获取微信快到期账号
  242. public Page<User> listByWechatExpire(int page, int size, Date expire){
  243. Example example = new Example(User.class);
  244. example.and(
  245. example.createCriteria()
  246. .andLessThan("wechatExpireTime", expire)
  247. );
  248. return page(()->select(userMapper, example), page, size);
  249. }
  250. // 通过手机号获取用户
  251. public User getByMobile(String area, String mobile){
  252. User user = User.builder().area(area).mobile(mobile).build();
  253. return one(userMapper, user);
  254. }
  255. // 通过邮箱获取用户
  256. public User getByEmail(String email){
  257. User user = User.builder().email(email).build();
  258. return one(userMapper, user);
  259. }
  260. // 通过身份证获取用户
  261. public User getByIdentity(String identity){
  262. User user = User.builder().realIdentity(identity).build();
  263. return one(userMapper, user);
  264. }
  265. // 通过openid获取用户
  266. public User getByOpen(String openId, String unionId, String platform){
  267. String platformField;
  268. String unionField;
  269. switch(platform){
  270. case "wechat_pc":
  271. platformField = PLATORM_WECHAT_PC;
  272. unionField = PLATORM_WECHAT;
  273. break;
  274. case "wechat_native":
  275. platformField = PLATORM_WECHAT_NATIVE;
  276. unionField = PLATORM_WECHAT;
  277. break;
  278. default:
  279. throw new ParameterException("第三方平台"+platform+"不支持");
  280. }
  281. Example example = new Example(User.class);
  282. example.or(
  283. example.createCriteria()
  284. .andEqualTo(platformField, openId)
  285. );
  286. if (unionId != null && !unionId.isEmpty()){
  287. example.or(
  288. example.createCriteria()
  289. .andEqualTo(unionField, unionId)
  290. );
  291. }
  292. return one(userMapper, example);
  293. }
  294. /**
  295. * 通过邀请码或者手机好获取邀请人
  296. * @param inviteCode
  297. * @return
  298. */
  299. public User getByInviteCode(String inviteCode){
  300. User user = new User();
  301. Example example = new Example(User.class);
  302. // 查找手机或邀请码
  303. example.and(
  304. example.createCriteria()
  305. .orEqualTo("invite_code", inviteCode)
  306. .orEqualTo("mobile", inviteCode)
  307. );
  308. return one(userMapper, example);
  309. }
  310. /**
  311. * 获取备考统计:身份
  312. * @return
  313. */
  314. public List<UserPrepareRelation> statPrepareStatus(){
  315. return userRelationMapper.groupPrepareString("prepare_status");
  316. }
  317. /**
  318. * 获取备考统计:目标分数
  319. * @return
  320. */
  321. public List<UserPrepareRelation> statPrepareGoal(){
  322. List<UserPrepareRelation> relations = userRelationMapper.groupPrepareInteger("prepare_goal");
  323. // 按考分分组
  324. List<UserPrepareRelation> userPrepareRelationList = new ArrayList<>();
  325. Integer[] goalGroup = new Integer[]{600, 650, 700, 750};
  326. for(Integer goal : goalGroup){
  327. UserPrepareRelation real = new UserPrepareRelation();
  328. real.setI(goal);
  329. real.setNumber(0);
  330. userPrepareRelationList.add(real);
  331. }
  332. for(UserPrepareRelation relation : relations){
  333. int goal = -1;
  334. for(int i = 0; i < goalGroup.length; i++){
  335. // 根据考分判断层级
  336. if (goalGroup[i] > relation.getI()){
  337. // 属于上一层
  338. goal = i - 1;
  339. break;
  340. }
  341. }
  342. if (goal > 0){
  343. // 按层级归类
  344. UserPrepareRelation real = userPrepareRelationList.get(goal);
  345. real.setNumber(real.getNumber()+relation.getNumber());
  346. }
  347. }
  348. return userPrepareRelationList;
  349. }
  350. /**
  351. * 获取备考统计:考试时间
  352. * @return
  353. */
  354. public List<UserPrepareRelation> statPrepareExaminationTime(){
  355. return userRelationMapper.groupPrepareString("prepare_examination_time");
  356. }
  357. /**
  358. * 获取备考统计:出分时间
  359. * @return
  360. */
  361. public List<UserPrepareRelation> statPrepareScoreTime(){
  362. // 直接按不同时间段统计
  363. Example example;
  364. List<UserPrepareRelation> userPrepareRelationList = new ArrayList<>();
  365. Calendar calendar = Calendar.getInstance();
  366. // 一个月内
  367. calendar.setTime(new Date());
  368. calendar.add(Calendar.MONTH, 1);
  369. example = new Example(User.class);
  370. example.and(
  371. example.createCriteria()
  372. .andLessThan("prepareScoreTime", calendar.getTime())
  373. );
  374. UserPrepareRelation oneMonth = new UserPrepareRelation();
  375. oneMonth.setS(PrepareExaminationTime.ONE_MONTH.key);
  376. oneMonth.setNumber(count(userMapper, example));
  377. userPrepareRelationList.add(oneMonth);
  378. // 两个月内
  379. calendar.setTime(new Date());
  380. calendar.add(Calendar.MONTH, 2);
  381. example = new Example(User.class);
  382. example.and(
  383. example.createCriteria()
  384. .andLessThan("prepareScoreTime", calendar.getTime())
  385. );
  386. UserPrepareRelation twoMonth = new UserPrepareRelation();
  387. twoMonth.setS(PrepareExaminationTime.TWO_MONTH.key);
  388. twoMonth.setNumber(count(userMapper, example) - oneMonth.getNumber());
  389. userPrepareRelationList.add(twoMonth);
  390. // 三个月内
  391. calendar.setTime(new Date());
  392. calendar.add(Calendar.MONTH, 3);
  393. example = new Example(User.class);
  394. example.and(
  395. example.createCriteria()
  396. .andLessThan("prepareScoreTime", calendar.getTime())
  397. );
  398. UserPrepareRelation threeMonth = new UserPrepareRelation();
  399. threeMonth.setS(PrepareExaminationTime.THREE_MONTH.key);
  400. threeMonth.setNumber(count(userMapper, example) - twoMonth.getNumber());
  401. userPrepareRelationList.add(threeMonth);
  402. // 半年
  403. calendar.setTime(new Date());
  404. calendar.add(Calendar.MONTH, 6);
  405. example = new Example(User.class);
  406. example.and(
  407. example.createCriteria()
  408. .andLessThan("prepareScoreTime", calendar.getTime())
  409. );
  410. UserPrepareRelation sixMonth = new UserPrepareRelation();
  411. sixMonth.setS(PrepareExaminationTime.SIX_MONTH.key);
  412. sixMonth.setNumber(count(userMapper, example) - threeMonth.getNumber());
  413. userPrepareRelationList.add(sixMonth);
  414. // 一年
  415. calendar.setTime(new Date());
  416. calendar.add(Calendar.MONTH, 12);
  417. example = new Example(User.class);
  418. example.and(
  419. example.createCriteria()
  420. .andLessThan("prepareScoreTime", calendar.getTime())
  421. );
  422. UserPrepareRelation oneYear = new UserPrepareRelation();
  423. oneYear.setS(PrepareExaminationTime.ONE_YEAR.key);
  424. oneYear.setNumber(count(userMapper, example) - sixMonth.getNumber());
  425. userPrepareRelationList.add(oneYear);
  426. // 其他
  427. example = new Example(User.class);
  428. example.and(
  429. example.createCriteria()
  430. // 设置过备考信息的人:mapper中做为基本条件
  431. .andGreaterThan("prepareGoal", 0)
  432. .andIsNull("prepareScoreTime")
  433. );
  434. UserPrepareRelation other = new UserPrepareRelation();
  435. other.setS(PrepareExaminationTime.OTHER.key);
  436. other.setNumber(count(userMapper, example));
  437. userPrepareRelationList.add(other);
  438. return userPrepareRelationList;
  439. }
  440. private Map<String, String> adminMap = new HashMap<String, String>(){{
  441. put("", "u");
  442. }};
  443. /**
  444. * 获取购买过课程的用户
  445. * @param page
  446. * @param pageSize
  447. * @param keyword
  448. * @param courseId
  449. * @return
  450. */
  451. public Page<User> listAdminByCourse(int page, int pageSize, String keyword, Integer courseId, String order, DirectionStatus direction){
  452. if(order == null || order.isEmpty()){
  453. order = "id";
  454. }
  455. if(adminMap.containsKey(order)){
  456. order = adminMap.get(order)+".`"+Tools.underscoreName(order)+"`";
  457. }else{
  458. order = adminMap.get("")+".`"+Tools.underscoreName(order)+"`";
  459. }
  460. if (direction == null){
  461. direction = DirectionStatus.DESC;
  462. }
  463. String finalOrder = order;
  464. DirectionStatus finalDirection = direction;
  465. Page<User> p = page(()->{
  466. userRelationMapper.listByCourse(keyword, courseId, finalOrder, finalDirection.key);
  467. }, page, pageSize);
  468. Collection ids = Transform.getIds(p, User.class, "id");
  469. // 获取详细数据
  470. List<User> list = select(ids);
  471. Transform.replace(p, list, User.class, "id");
  472. return p;
  473. }
  474. /**
  475. * 获取总用户数
  476. * @return
  477. */
  478. public Integer count(){
  479. Example example = new Example(User.class);
  480. return count(userMapper, example);
  481. }
  482. /**
  483. * 累加支付金额
  484. * @param userId
  485. * @param money
  486. */
  487. public void accumulation(Integer userId, BigDecimal money){
  488. userRelationMapper.accumulation(userId, money);
  489. }
  490. public Page<User> listAdmin(int page, int pageSize, String keyword, Boolean real, Boolean wechat, Boolean prepare, Date startTime, Date endTime, String order, DirectionStatus direction){
  491. Example example = new Example(User.class);
  492. if(keyword != null)
  493. example.and(
  494. example.createCriteria()
  495. .orLike("id", "%"+keyword+"%")
  496. .orLike("mobile", "%"+keyword+"%")
  497. );
  498. if (real != null)
  499. example.and(
  500. example.createCriteria().andEqualTo("realStatus", real?1:0)
  501. );
  502. if (wechat != null)
  503. example.and(
  504. wechat ? example.createCriteria().andNotEqualTo("wechatUnionid", "") : example.createCriteria().andEqualTo("wechatUnionid", "")
  505. );
  506. if (prepare != null)
  507. example.and(
  508. prepare ? example.createCriteria().andIsNotNull("prepareTime"):example.createCriteria().andIsNull("prepareTime")
  509. );
  510. if (startTime != null){
  511. example.and(
  512. example.createCriteria().andGreaterThanOrEqualTo("createTime", startTime)
  513. );
  514. }
  515. if(endTime != null){
  516. example.and(
  517. example.createCriteria().andLessThan("createTime", endTime)
  518. );
  519. }
  520. if(order==null||order.isEmpty()) order = "id";
  521. if (direction != null){
  522. switch(direction){
  523. case ASC:
  524. example.orderBy(order).asc();
  525. break;
  526. case DESC:
  527. default:
  528. example.orderBy(order).desc();
  529. }
  530. } else {
  531. example.orderBy(order).desc();
  532. }
  533. return select(userMapper, example, page, pageSize);
  534. }
  535. public boolean equalsPassword(User user, String password){
  536. return Objects.equals(user.getPassword(), Tools.stringMD5(Tools.stringMD5(password)));
  537. }
  538. public boolean changePassword(User user, String password){
  539. User updateUser = User.builder().id(user.getId()).password(Tools.stringMD5(Tools.stringMD5(password))).build();
  540. int result = update(userMapper, updateUser);
  541. return result > 0;
  542. }
  543. public User add(User user){
  544. if(user.getPassword() != null) user.setPassword(Tools.stringMD5(user.getPassword()));
  545. int result = insert(userMapper, user);
  546. user = one(userMapper, user.getId());
  547. if(user == null){
  548. throw new SystemException("用户添加失败");
  549. }
  550. return user;
  551. }
  552. public User edit(User user){
  553. User in = one(userMapper, user.getId());
  554. if(in == null){
  555. throw new ParameterException("用户不存在");
  556. }
  557. if(user.getPassword() != null) user.setPassword(Tools.stringMD5(user.getPassword()));
  558. int result = update(userMapper, user);
  559. return user;
  560. }
  561. public boolean delete(Number id){
  562. User in = one(userMapper, id);
  563. if(in == null){
  564. throw new ParameterException("用户不存在");
  565. }
  566. int result = delete(userMapper, id);
  567. return result > 0;
  568. }
  569. public User get(Number id){
  570. User in = one(userMapper, id);
  571. if(in == null){
  572. throw new ParameterException("用户不存在");
  573. }
  574. return in;
  575. }
  576. public Page<User> select(int page, int pageSize){return select(userMapper, page, pageSize);
  577. }
  578. public Page<User> select(Integer[] ids){
  579. return page(()->select(userMapper, ids), 1, ids.length);
  580. }
  581. public List<User> select(Collection ids){
  582. return select(userMapper, ids);
  583. }
  584. }