liujie
2026-07-31 0cf54692efc067adca7fa4308af241f723930b58
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package com.tourguide.modules.user.service;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tourguide.common.api.PageResponse;
import com.tourguide.common.exception.BusinessException;
import com.tourguide.common.validation.ChineseIdCardValidator;
import com.tourguide.modules.order.dto.VisitorOrderStatRow;
import com.tourguide.modules.order.mapper.TourOrderMapper;
import com.tourguide.modules.review.dto.VisitorReviewStatRow;
import com.tourguide.modules.review.mapper.OrderReviewMapper;
import com.tourguide.modules.user.dto.MiniProfileUpdateRequest;
import com.tourguide.modules.user.dto.MiniRealNameUpdateRequest;
import com.tourguide.modules.user.dto.MiniUserDetailResponse;
import com.tourguide.modules.user.dto.MiniUserResponse;
import com.tourguide.modules.user.entity.MiniUser;
import com.tourguide.modules.user.mapper.MiniUserMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
 
/** 仅处理小程序用户,管理后台只能通过本服务执行查询或状态管理。 */
@Service
public class MiniUserService {
    private final MiniUserMapper userMapper;
    private final TourOrderMapper orderMapper;
    private final OrderReviewMapper reviewMapper;
 
    public MiniUserService(MiniUserMapper userMapper, TourOrderMapper orderMapper, OrderReviewMapper reviewMapper) {
        this.userMapper = userMapper;
        this.orderMapper = orderMapper;
        this.reviewMapper = reviewMapper;
    }
 
    public MiniUser findEnabledById(Long id) {
        MiniUser user = userMapper.selectById(id);
        if (user == null || user.getStatus() == null || user.getStatus() != 1) {
            throw new BusinessException(401, "小程序用户不存在或已停用");
        }
        return user;
    }
 
    public MiniUser findByUsername(String username) {
        return userMapper.selectOne(new LambdaQueryWrapper<MiniUser>().eq(MiniUser::getUsername, username).last("LIMIT 1"));
    }
 
    public PageResponse<MiniUserResponse> page(long page, long size, String keyword, Integer status,
                                                LocalDate startDate, LocalDate endDate) {
        LambdaQueryWrapper<MiniUser> query = new LambdaQueryWrapper<MiniUser>();
        if (StringUtils.hasText(keyword)) {
            String searchKeyword = keyword.trim();
            query.and(wrapper -> {
                wrapper.like(MiniUser::getUsername, searchKeyword)
                        .or().like(MiniUser::getNickname, searchKeyword)
                        .or().like(MiniUser::getMobile, searchKeyword);
                if (searchKeyword.matches("\\d+")) {
                    wrapper.or().eq(MiniUser::getId, Long.valueOf(searchKeyword));
                }
            });
        }
        query.eq(status != null, MiniUser::getStatus, status)
                .ge(startDate != null, MiniUser::getCreatedAt, startDate == null ? null : startDate.atStartOfDay())
                .lt(endDate != null, MiniUser::getCreatedAt, endDate == null ? null : endDate.plusDays(1).atStartOfDay())
                .orderByDesc(MiniUser::getId);
        Page<MiniUser> result = userMapper.selectPage(new Page<MiniUser>(page, size), query);
        List<MiniUser> users = result.getRecords();
        Map<Long, UserStats> statsByUserId = loadUserStats(
                users.stream().map(MiniUser::getId).collect(Collectors.toList()));
        return new PageResponse<MiniUserResponse>(users.stream()
                .map(user -> MiniUserResponse.from(user, statsOf(statsByUserId, user.getId()).orderCount))
                .collect(Collectors.toList()),
                result.getTotal(), result.getCurrent(), result.getSize());
    }
 
    public MiniUserDetailResponse detail(Long id) {
        MiniUser user = userMapper.selectById(id);
        if (user == null) { throw new BusinessException(404, "游客不存在"); }
        UserStats stats = statsOf(loadUserStats(Collections.singletonList(id)), id);
        return MiniUserDetailResponse.from(user, stats.orderCount, stats.totalConsumption, stats.reviewCount);
    }
 
    private Map<Long, UserStats> loadUserStats(List<Long> visitorIds) {
        if (visitorIds.isEmpty()) { return Collections.emptyMap(); }
        Map<Long, UserStats> statsByUserId = new HashMap<Long, UserStats>();
        for (Long visitorId : visitorIds) {
            statsByUserId.put(visitorId, new UserStats());
        }
        List<VisitorOrderStatRow> orderRows = orderMapper.selectVisitorOrderStats(visitorIds);
        if (orderRows != null) {
            for (VisitorOrderStatRow row : orderRows) {
                if (row.getVisitorId() == null) { continue; }
                UserStats stats = statsByUserId.computeIfAbsent(row.getVisitorId(), ignored -> new UserStats());
                stats.orderCount = defaultLong(row.getOrderCount());
                stats.totalConsumption = row.getTotalConsumption() == null ? BigDecimal.ZERO : row.getTotalConsumption();
            }
        }
        List<VisitorReviewStatRow> reviewRows = reviewMapper.selectVisitorReviewStats(visitorIds);
        if (reviewRows != null) {
            for (VisitorReviewStatRow row : reviewRows) {
                if (row.getVisitorId() == null) { continue; }
                statsByUserId.computeIfAbsent(row.getVisitorId(), ignored -> new UserStats())
                        .reviewCount = defaultLong(row.getReviewCount());
            }
        }
        return statsByUserId;
    }
 
    private static UserStats statsOf(Map<Long, UserStats> statsByUserId, Long userId) {
        UserStats stats = statsByUserId.get(userId);
        return stats == null ? new UserStats() : stats;
    }
 
    private static long defaultLong(Long value) {
        return value == null ? 0L : value;
    }
 
    @Transactional
    public MiniUserResponse updateProfile(Long id, MiniProfileUpdateRequest request) {
        MiniUser user = findEnabledById(id);
        user.setNickname(request.getNickname().trim());
        userMapper.updateById(user);
        return MiniUserResponse.from(user);
    }
 
    @Transactional
    public MiniUserResponse updateAvatar(Long id, String avatar) {
        MiniUser user = findEnabledById(id);
        user.setAvatar(avatar);
        userMapper.updateById(user);
        return MiniUserResponse.from(user);
    }
 
    @Transactional
    public MiniUserResponse updateRealName(Long id, MiniRealNameUpdateRequest request) {
        MiniUser user = findEnabledById(id);
        String idCardNo = request.getIdCardNo().trim().toUpperCase();
        if (!ChineseIdCardValidator.isValid(idCardNo)) {
            throw new BusinessException(400, "身份证号格式不正确");
        }
        if (userMapper.selectCount(new LambdaQueryWrapper<MiniUser>()
                .eq(MiniUser::getIdCardNo, idCardNo)
                .ne(MiniUser::getId, id)) > 0) {
            throw new BusinessException(409, "该身份证号已被其他账号使用");
        }
        user.setRealName(request.getRealName().trim());
        user.setIdCardNo(idCardNo);
        userMapper.updateById(user);
        return MiniUserResponse.from(user);
    }
 
    @Transactional
    public void updateStatus(Long id, Integer status) {
        MiniUser user = userMapper.selectById(id);
        if (user == null) { throw new BusinessException(404, "小程序用户不存在"); }
        user.setStatus(status);
        userMapper.updateById(user);
    }
 
    private static final class UserStats {
        private long orderCount;
        private BigDecimal totalConsumption = BigDecimal.ZERO;
        private long reviewCount;
    }
}