zjk
10 小时以前 8ea6a7dd1fcdbd4e22c564ed63a5a8023510161a
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
<?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.ruoyi.user.mapper.AccountUserMapper">
 
    <resultMap type="AccountUser" id="AccountUserResult">
        <id     property="userId"          column="user_id"           />
        <result property="userName"        column="username"          />
        <result property="phone"           column="phone"             />
        <result property="password"        column="password"          />
        <result property="nickname"        column="nickname"          />
        <result property="avatar"          column="avatar"            />
        <result property="gender"          column="gender"            />
        <result property="birthday"        column="birthday"          />
        <result property="status"          column="status"            />
        <result property="systemLevel"     column="system_level"      />
        <result property="subjectType"     column="subject_type"      />
        <result property="authStatus"      column="auth_status"       />
        <result property="companyAuthStatus" column="company_auth_status" />
        <result property="city"            column="city"              />
        <result property="referrerId"      column="referrer_id"       />
        <result property="referrerBindTime" column="referrer_bind_time" />
        <result property="supplierId"      column="supplier_id"       />
        <result property="supplierBindTime" column="supplier_bind_time" />
        <result property="buyOrderCount"     column="buy_order_count"      />
        <result property="buyAmountTotal"    column="buy_amount_total"     />
        <result property="sellOrderCount"    column="sell_order_count"     />
        <result property="sellAmountTotal"   column="sell_amount_total"    />
        <result property="purchaseOrderCount" column="purchase_order_count" />
        <result property="purchaseAmountTotal" column="purchase_amount_total" />
        <result property="sellProfitTotal"   column="sell_profit_total"    />
        <result property="virtualStockQty"   column="virtual_stock_qty"    />
        <result property="virtualStockValue" column="virtual_stock_value"  />
        <result property="delFlag"         column="del_flag"          />
        <result property="myReferralCode"   column="my_referral_code"   />
        <result property="providerFlag"    column="provider_flag"     />
        <result property="createBy"        column="create_by"         />
        <result property="createTime"      column="create_time"       />
        <result property="updateBy"        column="update_by"         />
        <result property="updateTime"      column="update_time"       />
        <result property="remark"          column="remark"            />
    </resultMap>
 
    <!-- 详情专用:在 AccountUserResult 基础上追加推荐人/供货人的派生列 -->
    <resultMap type="AccountUserVo" id="AccountUserVoResult" extends="AccountUserResult">
        <result property="referrerPhone"    column="referrer_phone"    />
        <result property="referrerLevel"    column="referrer_level"    />
        <result property="referrerNickname" column="referrer_nickname" />
        <result property="supplierPhone"    column="supplier_phone"    />
        <result property="supplierLevel"    column="supplier_level"    />
        <result property="supplierNickname" column="supplier_nickname" />
    </resultMap>
 
    <sql id="selectAccountUserVo">
        select user_id, username, phone, password, nickname, avatar, gender, birthday, status,
               system_level, subject_type, auth_status, company_auth_status, city,
               referrer_id, referrer_bind_time, supplier_id, supplier_bind_time,
               buy_order_count, buy_amount_total, sell_order_count, sell_amount_total,
               purchase_order_count, purchase_amount_total, sell_profit_total,
               virtual_stock_qty, virtual_stock_value,my_referral_code,
               del_flag, provider_flag, create_by, create_time, update_by, update_time, remark
        from account_user
    </sql>
 
    <select id="selectAccountUserList" parameterType="AccountUser" resultMap="AccountUserResult">
        <include refid="selectAccountUserVo"/>
        <where>
            del_flag = '0'
            <if test="userId != null"> and user_id = #{userId}</if>
            <!-- 手机号按完整号码精确匹配:列表筛选是「用户手机」,C 端按全号检索是常态;
                 且 like '%x%' 的前缀通配会让 idx_account_user_phone 完全失效。 -->
            <if test="phone != null and phone != ''"> and phone = #{phone}</if>
            <if test="userName != null and userName != ''"> and username like concat('%', #{userName}, '%')</if>
            <if test="nickname != null and nickname != ''"> and nickname like concat('%', #{nickname}, '%')</if>
            <if test="gender != null and gender != ''"> and gender = #{gender}</if>
            <if test="status != null and status != ''"> and status = #{status}</if>
            <if test="systemLevel != null and systemLevel != ''"> and system_level = #{systemLevel}</if>
            <if test="authStatus != null and authStatus != ''"> and auth_status = #{authStatus}</if>
            <if test="subjectType != null and subjectType != ''"> and subject_type = #{subjectType}</if>
            <!-- 城市按名称精确匹配,选项来自 account_city 的启用城市:
                 城市表按「省 + 市」两列存,但会员表只有 city(市级、无省份),故筛选口径就是市名。
                 原先那套「只选省时把该省全部市名拼成 IN」的逻辑随城市主数据扁平化一起去掉了。 -->
            <if test="city != null and city != ''"> and city = #{city}</if>
            <!-- 时间范围必须是 sargable 写法:原来的 date_format(create_time,'%y%m%d') 把列包在函数里,
                 create_time 上的索引用不上,数据量上来后是全表扫。 -->
            <if test="params.beginTime != null and params.beginTime != ''">
                and create_time &gt;= #{params.beginTime}
            </if>
            <if test="params.endTime != null and params.endTime != ''">
                and create_time &lt; date_add(#{params.endTime}, interval 1 day)
            </if>
        </where>
        order by user_id desc
    </select>
 
    <select id="selectAccountUserById" parameterType="Long" resultMap="AccountUserResult">
        <include refid="selectAccountUserVo"/>
        where del_flag = '0' and user_id = #{userId}
    </select>
 
    <select id="selectAccountUserByUsername" parameterType="String" resultMap="AccountUserResult">
        <include refid="selectAccountUserVo"/>
        where del_flag = '0' and username = #{userName}
    </select>
 
    <select id="selectAccountUserByPhone" parameterType="String" resultMap="AccountUserResult">
        <include refid="selectAccountUserVo"/>
        where del_flag = '0' and phone = #{phone}
    </select>
 
    <!--
        详情查询:会员本体 + 推荐人/供货人的手机号、等级、昵称。
        两次自连接都走 user_id 主键,成本极低。
        条件里的 del_flag = '0' 不可省:推荐人可能已被逻辑删除(造数脚本的 user_id=125
        引用已删除的 124 就是这种边界行),漏掉它会关联出已删除用户的信息。
        仅供详情使用——列表带分页,挂 join 会干扰 PageHelper 的 count SQL。
    -->
    <select id="selectAccountUserVoById" parameterType="Long" resultMap="AccountUserVoResult">
        select u.*,
               r.phone        as referrer_phone,
               r.system_level as referrer_level,
               r.nickname     as referrer_nickname,
               s.phone        as supplier_phone,
               s.system_level as supplier_level,
               s.nickname     as supplier_nickname
        from account_user u
        left join account_user r on r.user_id = u.referrer_id and r.del_flag = '0'
        left join account_user s on s.user_id = u.supplier_id and s.del_flag = '0'
        where u.del_flag = '0' and u.user_id = #{userId}
    </select>
 
    <insert id="insertAccountUser" parameterType="AccountUser" useGeneratedKeys="true" keyProperty="userId">
        insert into account_user(
            <if test="userName != null and userName != ''">username,</if>
            <if test="phone != null and phone != ''">phone,</if>
            <if test="password != null and password != ''">password,</if>
            <if test="nickname != null and nickname != ''">nickname,</if>
            <if test="avatar != null and avatar != ''">avatar,</if>
            <if test="gender != null and gender != ''">gender,</if>
            <if test="birthday != null">birthday,</if>
            <if test="status != null and status != ''">status,</if>
        <if test="myReferralCode != null and myReferralCode != ''">my_referral_code,</if>
        <if test="systemLevel != null and systemLevel != ''">system_level,</if>
            <if test="subjectType != null and subjectType != ''">subject_type,</if>
            <if test="authStatus != null and authStatus != ''">auth_status,</if>
            <if test="city != null and city != ''">city,</if>
            <if test="referrerId != null">referrer_id,</if>
            <if test="referrerBindTime != null">referrer_bind_time,</if>
            <if test="supplierId != null">supplier_id,</if>
            <if test="supplierBindTime != null">supplier_bind_time,</if>
            <if test="remark != null">remark,</if>
            <if test="createBy != null and createBy != ''">create_by,</if>
            create_time
        )values(
            <if test="userName != null and userName != ''">#{userName},</if>
            <if test="phone != null and phone != ''">#{phone},</if>
            <if test="password != null and password != ''">#{password},</if>
            <if test="nickname != null and nickname != ''">#{nickname},</if>
            <if test="avatar != null and avatar != ''">#{avatar},</if>
            <if test="gender != null and gender != ''">#{gender},</if>
            <if test="birthday != null">#{birthday},</if>
            <if test="status != null and status != ''">#{status},</if>
        <if test="myReferralCode != null and myReferralCode != ''">#{myReferralCode},</if>
 
        <if test="systemLevel != null and systemLevel != ''">#{systemLevel},</if>
            <if test="subjectType != null and subjectType != ''">#{subjectType},</if>
            <if test="authStatus != null and authStatus != ''">#{authStatus},</if>
            <if test="city != null and city != ''">#{city},</if>
            <if test="referrerId != null">#{referrerId},</if>
            <if test="referrerBindTime != null">#{referrerBindTime},</if>
            <if test="supplierId != null">#{supplierId},</if>
            <if test="supplierBindTime != null">#{supplierBindTime},</if>
            <if test="remark != null">#{remark},</if>
            <if test="createBy != null and createBy != ''">#{createBy},</if>
            sysdate()
        )
    </insert>
 
    <update id="updateAccountUser" parameterType="AccountUser">
        update account_user
        <set>
            <if test="userName != null and userName != ''">username = #{userName},</if>
            <if test="phone != null and phone != ''">phone = #{phone},</if>
            <if test="password != null and password != ''">password = #{password},</if>
            <if test="nickname != null">nickname = #{nickname},</if>
            <if test="avatar != null">avatar = #{avatar},</if>
            <if test="gender != null and gender != ''">gender = #{gender},</if>
            <if test="birthday != null">birthday = #{birthday},</if>
            <if test="status != null and status != ''">status = #{status},</if>
            <if test="myReferralCode != null and myReferralCode != ''">my_referral_code = #{myReferralCode},</if>
            <if test="systemLevel != null and systemLevel != ''">system_level = #{systemLevel},</if>
            <if test="subjectType != null and subjectType != ''">subject_type = #{subjectType},</if>
            <if test="authStatus != null and authStatus != ''">auth_status = #{authStatus},</if>
            <if test="city != null">city = #{city},</if>
            <if test="referrerId != null">referrer_id = #{referrerId},</if>
            <if test="referrerBindTime != null">referrer_bind_time = #{referrerBindTime},</if>
            <if test="supplierId != null">supplier_id = #{supplierId},</if>
            <if test="supplierBindTime != null">supplier_bind_time = #{supplierBindTime},</if>
            <if test="remark != null">remark = #{remark},</if>
            <if test="updateBy != null">update_by = #{updateBy},</if>
            update_time = sysdate()
        </set>
        where user_id = #{userId} and del_flag = '0'
    </update>
 
    <!--
        关系修改专用。不能复用 updateAccountUser:一是那里按 <if> 拼列,传不全也没问题,
        但业务上不该允许借这个口子改别的字段;二是 Controller 侧的统一校验链路
        (@NotBlank userName、用户名/手机号查重)会拦下只传关系的请求。
        referrerId / supplierId 传 null 即为解除绑定(<set> 里直接赋值,不走 <if>)。
    -->
    <update id="updateUserRelation" parameterType="AccountUser">
        update account_user
        <set>
            referrer_id        = #{referrerId},
            referrer_bind_time = #{referrerBindTime},
            supplier_id        = #{supplierId},
            supplier_bind_time = #{supplierBindTime},
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
            update_time = sysdate()
        </set>
        where user_id = #{userId} and del_flag = '0'
    </update>
 
    <!-- 状态变更专用:同上,避免与整体编辑共用校验链路 -->
    <update id="updateUserStatus" parameterType="AccountUser">
        update account_user
        <set>
            status = #{status},
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
            update_time = sysdate()
        </set>
        where user_id = #{userId} and del_flag = '0'
    </update>
 
    <!-- 主体认证审核通过/驳回后回写认证状态,只动 auth_status 一列 -->
    <update id="updateUserAuthStatus" parameterType="AccountUser">
        update account_user
        <set>
            auth_status = #{authStatus},
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
            update_time = sysdate()
        </set>
        where user_id = #{userId} and del_flag = '0'
    </update>
 
    <!--
        企业/个体认证审核通过/驳回后回写,只动 company_auth_status 一列;
        与 updateUserAuthStatus 分开是因为企业认证状态与个人认证状态是两列,不能互相覆盖。
        审核通过时服务层把 subjectType 一起带上(=2/3),此处 <if> 才写 subject_type;
        驳回时服务层不传 subjectType,列保持不变——否则会把未通过审核的会员标成企业主体。
    -->
    <update id="updateUserCompanyAuthStatus" parameterType="AccountUser">
        update account_user
        <set>
            company_auth_status = #{companyAuthStatus},
            <if test="subjectType != null and subjectType != ''">subject_type = #{subjectType},</if>
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
            update_time = sysdate()
        </set>
        where user_id = #{userId} and del_flag = '0'
    </update>
 
    <!--
        头像更新专用:只动 avatar 一列。绝不能挂到共享的 updateAccountUser 上——
        那是后台编辑接口(/account/user)的入口,挂上去等于请求体带个 avatar 就能改任意会员头像。
        与 updateUserStatus / updateUserAuthStatus 同一套路,头像只能由会员本人在 C 端接口里改写。
    -->
    <update id="updateUserAvatar" parameterType="AccountUser">
        update account_user
        <set>
            avatar = #{avatar},
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
            update_time = sysdate()
        </set>
        where user_id = #{userId} and del_flag = '0'
    </update>
 
    <!--
        服务商标识专用:与 updateUserAuthStatus 同一个套路——别的模块要给会员打标识,
        就单开一条只写该列的语句,绝不挂到共享的 insertAccountUser / updateAccountUser 上
        (那两个按 <if> 拼列,挂上去等于 /account/user 的请求体里带个 providerFlag 就能自己打标)。
        当前唯一调用方是服务商模块。
    -->
    <update id="updateUserProviderFlag" parameterType="AccountUser">
        update account_user
        <set>
            provider_flag = #{providerFlag},
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
            update_time = sysdate()
        </set>
        where user_id = #{userId} and del_flag = '0'
    </update>
 
    <!-- 逻辑删除:保留行,仅置删除标志 -->
    <update id="deleteAccountUserById" parameterType="Long">
        update account_user set del_flag = '2' where user_id = #{userId}
    </update>
 
    <update id="deleteAccountUserByIds" parameterType="Long">
        update account_user set del_flag = '2' where user_id in
        <foreach item="userId" collection="array" open="(" separator="," close=")">
            #{userId}
        </foreach>
    </update>
 
    <!--
        「我邀请的」列表:按 referrer_id 过滤 + referrer_bind_time 倒序,命中 idx_account_user_referrer。
        显式列出所需列(禁 SELECT *);不挂 join,避免干扰 PageHelper 的 count SQL。
        referrer_bind_time 为空的历史行,MySQL 中 NULL 视为最小值,倒序时自然排在最后。
    -->
    <select id="selectInvitees" parameterType="Long" resultMap="AccountUserResult">
        select user_id, nickname, avatar, phone, system_level, referrer_bind_time, buy_amount_total
        from account_user
        where del_flag = '0' and referrer_id = #{referrerId}
        order by referrer_bind_time desc
    </select>
 
    <resultMap type="MemberOptionVo" id="MemberOptionVoResult">
        <id     property="userId"      column="user_id"      />
        <result property="memberName"  column="member_name"  />
        <result property="phone"       column="phone"        />
        <result property="systemLevel" column="system_level" />
    </resultMap>
 
    <!--
        城市公司档案表单的「关联用户信息」下拉:只搜 L4(档案的准入条件)。
        显示名取认证表的真实姓名,无认证资料时回落昵称;两者都为空则前端按会员编号展示。
        limit 20 是必要的:下拉是随打随搜的,不设上限等于把「输一个 1」变成全表 like。
        该查询不分页,故 join 不会干扰 PageHelper 的 count SQL。
    -->
    <select id="selectL4MemberOptions" resultMap="MemberOptionVoResult">
        select u.user_id, u.phone, u.system_level,
               coalesce(nullif(cert.real_name, ''), u.nickname) as member_name
        from account_user u
        left join account_certification cert on cert.user_id = u.user_id and cert.del_flag = '0'
        where u.del_flag = '0' and u.system_level = 'L4'
        <if test="keyword != null and keyword != ''">
            and (u.phone    like concat('%', #{keyword}, '%')
                 or u.nickname   like concat('%', #{keyword}, '%')
                 or cert.real_name like concat('%', #{keyword}, '%')
                 <if test="keywordId != null"> or u.user_id = #{keywordId}</if>)
        </if>
        order by u.user_id desc
        limit 20
    </select>
 
    <!--
        修改供货人的「新供货人」下拉:只搜等级严格高于该会员的会员。
        levels 由服务层按 AccountUser.levelsAbove 算好,且必须非空——空集合会拼成
        in () 的语法错误(该会员已是最高等级时服务层提前返回,走不到这条语句)。
 
        其余口径刻意与 selectL4MemberOptions 保持一致:显示名取认证表真实姓名、无认证
        回落昵称;手机号/昵称/姓名模糊 + 纯数字 keywordId 等值命中编号;limit 20 是必要的
        (随打随搜不设上限,等于把「输一个 1」变成全表 like)。
        user_id != #{userId} 既保证不自环,也顺带把会员自己从候选里摘掉。
    -->
    <select id="selectSupplierCandidates" resultMap="MemberOptionVoResult">
        select u.user_id, u.phone, u.system_level,
               coalesce(nullif(cert.real_name, ''), u.nickname) as member_name
        from account_user u
        left join account_certification cert on cert.user_id = u.user_id and cert.del_flag = '0'
        where u.del_flag = '0'
          and u.user_id != #{userId}
          and u.system_level in
        <foreach item="level" collection="levels" open="(" separator="," close=")">
            #{level}
        </foreach>
        <if test="keyword != null and keyword != ''">
            and (u.phone    like concat('%', #{keyword}, '%')
                 or u.nickname   like concat('%', #{keyword}, '%')
                 or cert.real_name like concat('%', #{keyword}, '%')
                 <if test="keywordId != null"> or u.user_id = #{keywordId}</if>)
        </if>
        order by u.user_id desc
        limit 20
    </select>
 
</mapper>