From 3b64bcc08e7103006599278f7fe3d8cdeebf30b5 Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期五, 09 十月 2026 17:59:52 +0800
Subject: [PATCH] init

---
 hugray-admin/src/api/tool/gen.js                                                                             |   21 
 hugray-server/sql/hugray_account_20261009.sql                                                                |   34 +
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java |   88 +++
 hugray-admin/src/api/user/account.js                                                                         |   48 +
 hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/bootstrap.yml                                      |   27 
 hugray-admin/src/api/menu.js                                                                                 |    2 
 hugray-server/ruoyi-modules/ruoyi-user/pom.xml                                                               |   94 +++
 hugray-admin/src/api/monitor/job.js                                                                          |   15 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/RuoYiUserApplication.java                |   32 +
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java            |   52 +
 hugray-server/bin/stop-all.bat                                                                               |   33 +
 hugray-server/ruoyi-gateway/src/main/resources/bootstrap.yml                                                 |    2 
 hugray-admin/src/api/monitor/jobLog.js                                                                       |    7 
 hugray-admin/src/views/user/account/index.vue                                                                |  359 ++++++++++++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml                  |  122 ++++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java    |  131 ++++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java         |   49 +
 hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/logback.xml                                        |   74 ++
 hugray-admin/src/api/monitor/logininfor.js                                                                   |    9 
 hugray-admin/src/api/login.js                                                                                |   18 
 hugray-admin/src/store/modules/user.js                                                                       |    4 
 README.md                                                                                                    |   14 
 hugray-admin/src/api/monitor/online.js                                                                       |    5 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java                  |  193 ++++++
 hugray-server/bin/start-all.bat                                                                              |  148 +++++
 hugray-admin/src/api/monitor/operlog.js                                                                      |    7 
 hugray-server/ruoyi-modules/pom.xml                                                                          |    1 
 hugray-server/sql/hugray_menu_account_20261009.sql                                                           |   26 
 hugray-admin/vite.config.js                                                                                  |    2 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/banner.txt                                         |   10 
 30 files changed, 1,583 insertions(+), 44 deletions(-)

diff --git a/README.md b/README.md
index dd0df8f..2c95698 100644
--- a/README.md
+++ b/README.md
@@ -47,11 +47,12 @@
 
 | 模块 | 目录 | 说明 | 端口 |
 | --- | --- | --- | --- |
-| 网关 | `ruoyi-gateway` | 统一入口、路由转发、鉴权与限流 | 8080 |
+| 网关 | `ruoyi-gateway` | 统一入口、路由转发、鉴权与限流 | 8085 |
 | 认证服务 | `ruoyi-auth` | 登录、注册、令牌签发 | 9200 |
 | 系统服务 | `ruoyi-modules/ruoyi-system` | 用户 / 角色 / 菜单 / 部门 / 字典 / 日志等 | 9201 |
 | 代码生成 | `ruoyi-modules/ruoyi-gen` | 前后端代码一键生成 | 9202 |
 | 定时任务 | `ruoyi-modules/ruoyi-job` | Quartz 任务调度 | 9203 |
+| 用户服务 | `ruoyi-modules/ruoyi-user` | C 端会员用户(`account_user`,网关前缀 `/account`) | 9210 |
 | 文件服务 | `ruoyi-modules/ruoyi-file` | 本地 / MinIO 文件上传下载 | 9300 |
 | 监控服务 | `ruoyi-visual/ruoyi-monitor` | Spring Boot Admin 服务监控 | 9100 |
 
@@ -72,10 +73,14 @@
 
 - `docker/`:Nacos、MySQL、Redis、Nginx 及各服务的容器化配置,编排文件为 `docker/docker-compose.yml`
 - `docker/deploy.sh`、`docker/copy.sh`:一键部署与拷贝脚本
-- `bin/`:各服务的本地启动脚本(`run-gateway.bat`、`run-auth.bat`、`run-modules-*.bat`、`run-monitor.bat`)
+- `bin/`:本地启动脚本
+    - `start-all.bat`、`stop-all.bat`:一键启动/停止 Nacos、Redis 与全部服务(Windows)
+    - `run-gateway.bat`、`run-auth.bat`、`run-modules-*.bat`、`run-monitor.bat`:单服务前台启动
 - `sql/`:初始化脚本
     - `ry_20260417.sql`:业务库(`ry-cloud`)
     - `ry_config_20260918.sql`:Nacos 配置库
+    - `hugray_account_20261009.sql`:会员库 `hugray-account` 的 `account_user` 表
+    - `hugray_menu_account_20261009.sql`:用户服务菜单(`hugray` 库 `sys_menu`)
     - `quartz.sql`:定时任务表
     - `ry_seata_20210128.sql`:Seata 事务表
 
@@ -118,6 +123,8 @@
 ruoyi-gateway → ruoyi-auth → ruoyi-modules/ruoyi-system → ... → ruoyi-visual/ruoyi-monitor
 ```
 
+Windows 下可直接双击 `hugray-server/bin/start-all.bat`:脚本会自动拉起 Nacos、Redis,再依次启动全部 8 个服务并等待网关就绪;端口已被占用的服务自动跳过,因此可重复执行。停止用 `stop-all.bat`(Redis 作为常驻中间件不在此列)。两者的路径配置都在文件顶部的「配置区」,换机器时改那里即可。
+
 ### 4. 启动管理后台前端
 
 ```bash
@@ -136,11 +143,12 @@
 | 服务 | 端口 |
 | --- | --- |
 | 管理后台前端(开发) | 80 |
-| 网关 ruoyi-gateway | 8080 |
+| 网关 ruoyi-gateway | 8085 |
 | 认证 ruoyi-auth | 9200 |
 | 系统 ruoyi-system | 9201 |
 | 代码生成 ruoyi-gen | 9202 |
 | 定时任务 ruoyi-job | 9203 |
+| 用户服务 ruoyi-user | 9210 |
 | 文件服务 ruoyi-file | 9300 |
 | 监控 ruoyi-monitor | 9100 |
 | Nacos | 8848 / 9848 / 9849 |
diff --git a/hugray-admin/src/api/login.js b/hugray-admin/src/api/login.js
index 2165d2b..96ae171 100644
--- a/hugray-admin/src/api/login.js
+++ b/hugray-admin/src/api/login.js
@@ -1,5 +1,9 @@
 import request from '@/utils/request'
 
+// 注意:本前端对接的是 hugray-server 微服务网关,路径必须带云网关前缀
+// (校验码由网关自身提供 /code,认证走 /auth/**,用户与菜单走 /system/**),
+// 不能使用 RuoYi 单体版的 /login、/getInfo、/getRouters 等写法。
+
 // 登录方法
 export function login(username, password, code, uuid) {
   const data = {
@@ -9,7 +13,7 @@
     uuid
   }
   return request({
-    url: '/login',
+    url: '/auth/login',
     headers: {
       isToken: false,
       repeatSubmit: false
@@ -22,7 +26,7 @@
 // 注册方法
 export function register(data) {
   return request({
-    url: '/register',
+    url: '/auth/register',
     headers: {
       isToken: false
     },
@@ -34,7 +38,7 @@
 // 获取用户详细信息
 export function getInfo() {
   return request({
-    url: '/getInfo',
+    url: '/system/user/getInfo',
     method: 'get'
   })
 }
@@ -42,7 +46,7 @@
 // 解锁屏幕
 export function unlockScreen(password) {
   return request({
-    url: '/unlockscreen',
+    url: '/auth/unlockscreen',
     method: 'post',
     data: { password }
   })
@@ -51,15 +55,15 @@
 // 退出方法
 export function logout() {
   return request({
-    url: '/logout',
-    method: 'post'
+    url: '/auth/logout',
+    method: 'delete'
   })
 }
 
 // 获取验证码
 export function getCodeImg() {
   return request({
-    url: '/captchaImage',
+    url: '/code',
     headers: {
       isToken: false
     },
diff --git a/hugray-admin/src/api/menu.js b/hugray-admin/src/api/menu.js
index faef101..845efd7 100644
--- a/hugray-admin/src/api/menu.js
+++ b/hugray-admin/src/api/menu.js
@@ -3,7 +3,7 @@
 // 获取路由
 export const getRouters = () => {
   return request({
-    url: '/getRouters',
+    url: '/system/menu/getRouters',
     method: 'get'
   })
 }
\ No newline at end of file
diff --git a/hugray-admin/src/api/monitor/job.js b/hugray-admin/src/api/monitor/job.js
index 3815569..8bf388e 100644
--- a/hugray-admin/src/api/monitor/job.js
+++ b/hugray-admin/src/api/monitor/job.js
@@ -1,9 +1,10 @@
 import request from '@/utils/request'
 
+// 注意:本前端对接 hugray-server 云网关,路径与 RuoYi 单体版不同。
 // 查询定时任务调度列表
 export function listJob(query) {
   return request({
-    url: '/monitor/job/list',
+    url: '/schedule/job/list',
     method: 'get',
     params: query
   })
@@ -12,7 +13,7 @@
 // 查询定时任务调度详细
 export function getJob(jobId) {
   return request({
-    url: '/monitor/job/' + jobId,
+    url: '/schedule/job/' + jobId,
     method: 'get'
   })
 }
@@ -20,7 +21,7 @@
 // 新增定时任务调度
 export function addJob(data) {
   return request({
-    url: '/monitor/job',
+    url: '/schedule/job',
     method: 'post',
     data: data
   })
@@ -29,7 +30,7 @@
 // 修改定时任务调度
 export function updateJob(data) {
   return request({
-    url: '/monitor/job',
+    url: '/schedule/job',
     method: 'put',
     data: data
   })
@@ -38,7 +39,7 @@
 // 删除定时任务调度
 export function delJob(jobId) {
   return request({
-    url: '/monitor/job/' + jobId,
+    url: '/schedule/job/' + jobId,
     method: 'delete'
   })
 }
@@ -50,7 +51,7 @@
     status
   }
   return request({
-    url: '/monitor/job/changeStatus',
+    url: '/schedule/job/changeStatus',
     method: 'put',
     data: data
   })
@@ -64,7 +65,7 @@
     jobGroup
   }
   return request({
-    url: '/monitor/job/run',
+    url: '/schedule/job/run',
     method: 'put',
     data: data
   })
diff --git a/hugray-admin/src/api/monitor/jobLog.js b/hugray-admin/src/api/monitor/jobLog.js
index 6e0be61..ecebbc1 100644
--- a/hugray-admin/src/api/monitor/jobLog.js
+++ b/hugray-admin/src/api/monitor/jobLog.js
@@ -1,9 +1,10 @@
 import request from '@/utils/request'
 
+// 注意:本前端对接 hugray-server 云网关,路径与 RuoYi 单体版不同。
 // 查询调度日志列表
 export function listJobLog(query) {
   return request({
-    url: '/monitor/jobLog/list',
+    url: '/schedule/job/log/list',
     method: 'get',
     params: query
   })
@@ -12,7 +13,7 @@
 // 删除调度日志
 export function delJobLog(jobLogId) {
   return request({
-    url: '/monitor/jobLog/' + jobLogId,
+    url: '/schedule/job/log/' + jobLogId,
     method: 'delete'
   })
 }
@@ -20,7 +21,7 @@
 // 清空调度日志
 export function cleanJobLog() {
   return request({
-    url: '/monitor/jobLog/clean',
+    url: '/schedule/job/log/clean',
     method: 'delete'
   })
 }
diff --git a/hugray-admin/src/api/monitor/logininfor.js b/hugray-admin/src/api/monitor/logininfor.js
index 4d112b7..7c9224d 100644
--- a/hugray-admin/src/api/monitor/logininfor.js
+++ b/hugray-admin/src/api/monitor/logininfor.js
@@ -1,9 +1,10 @@
 import request from '@/utils/request'
 
+// 注意:本前端对接 hugray-server 云网关,路径与 RuoYi 单体版不同。
 // 查询登录日志列表
 export function list(query) {
   return request({
-    url: '/monitor/logininfor/list',
+    url: '/system/logininfor/list',
     method: 'get',
     params: query
   })
@@ -12,7 +13,7 @@
 // 删除登录日志
 export function delLogininfor(infoId) {
   return request({
-    url: '/monitor/logininfor/' + infoId,
+    url: '/system/logininfor/' + infoId,
     method: 'delete'
   })
 }
@@ -20,7 +21,7 @@
 // 解锁用户登录状态
 export function unlockLogininfor(userName) {
   return request({
-    url: '/monitor/logininfor/unlock/' + userName,
+    url: '/system/logininfor/unlock/' + userName,
     method: 'get'
   })
 }
@@ -28,7 +29,7 @@
 // 清空登录日志
 export function cleanLogininfor() {
   return request({
-    url: '/monitor/logininfor/clean',
+    url: '/system/logininfor/clean',
     method: 'delete'
   })
 }
diff --git a/hugray-admin/src/api/monitor/online.js b/hugray-admin/src/api/monitor/online.js
index bd22137..788177b 100644
--- a/hugray-admin/src/api/monitor/online.js
+++ b/hugray-admin/src/api/monitor/online.js
@@ -1,9 +1,10 @@
 import request from '@/utils/request'
 
+// 注意:本前端对接 hugray-server 云网关,路径与 RuoYi 单体版不同。
 // 查询在线用户列表
 export function list(query) {
   return request({
-    url: '/monitor/online/list',
+    url: '/system/online/list',
     method: 'get',
     params: query
   })
@@ -12,7 +13,7 @@
 // 强退用户
 export function forceLogout(tokenId) {
   return request({
-    url: '/monitor/online/' + tokenId,
+    url: '/system/online/' + tokenId,
     method: 'delete'
   })
 }
diff --git a/hugray-admin/src/api/monitor/operlog.js b/hugray-admin/src/api/monitor/operlog.js
index a04bca8..5df2c20 100644
--- a/hugray-admin/src/api/monitor/operlog.js
+++ b/hugray-admin/src/api/monitor/operlog.js
@@ -1,9 +1,10 @@
 import request from '@/utils/request'
 
+// 注意:本前端对接 hugray-server 云网关,路径与 RuoYi 单体版不同。
 // 查询操作日志列表
 export function list(query) {
   return request({
-    url: '/monitor/operlog/list',
+    url: '/system/operlog/list',
     method: 'get',
     params: query
   })
@@ -12,7 +13,7 @@
 // 删除操作日志
 export function delOperlog(operId) {
   return request({
-    url: '/monitor/operlog/' + operId,
+    url: '/system/operlog/' + operId,
     method: 'delete'
   })
 }
@@ -20,7 +21,7 @@
 // 清空操作日志
 export function cleanOperlog() {
   return request({
-    url: '/monitor/operlog/clean',
+    url: '/system/operlog/clean',
     method: 'delete'
   })
 }
diff --git a/hugray-admin/src/api/tool/gen.js b/hugray-admin/src/api/tool/gen.js
index 2075677..58d6822 100644
--- a/hugray-admin/src/api/tool/gen.js
+++ b/hugray-admin/src/api/tool/gen.js
@@ -1,9 +1,10 @@
 import request from '@/utils/request'
 
+// 注意:本前端对接 hugray-server 云网关,路径与 RuoYi 单体版不同。
 // 查询生成表数据
 export function listTable(query) {
   return request({
-    url: '/tool/gen/list',
+    url: '/code/gen/list',
     method: 'get',
     params: query
   })
@@ -11,7 +12,7 @@
 // 查询db数据库列表
 export function listDbTable(query) {
   return request({
-    url: '/tool/gen/db/list',
+    url: '/code/gen/db/list',
     method: 'get',
     params: query
   })
@@ -20,7 +21,7 @@
 // 查询表详细信息
 export function getGenTable(tableId) {
   return request({
-    url: '/tool/gen/' + tableId,
+    url: '/code/gen/' + tableId,
     method: 'get'
   })
 }
@@ -28,7 +29,7 @@
 // 修改代码生成信息
 export function updateGenTable(data) {
   return request({
-    url: '/tool/gen',
+    url: '/code/gen',
     method: 'put',
     data: data
   })
@@ -37,7 +38,7 @@
 // 导入表
 export function importTable(data) {
   return request({
-    url: '/tool/gen/importTable',
+    url: '/code/gen/importTable',
     method: 'post',
     params: data
   })
@@ -46,7 +47,7 @@
 // 创建表
 export function createTable(data) {
   return request({
-    url: '/tool/gen/createTable',
+    url: '/code/gen/createTable',
     method: 'post',
     params: data
   })
@@ -55,7 +56,7 @@
 // 预览生成代码
 export function previewTable(tableId) {
   return request({
-    url: '/tool/gen/preview/' + tableId,
+    url: '/code/gen/preview/' + tableId,
     method: 'get'
   })
 }
@@ -63,7 +64,7 @@
 // 删除表数据
 export function delTable(tableId) {
   return request({
-    url: '/tool/gen/' + tableId,
+    url: '/code/gen/' + tableId,
     method: 'delete'
   })
 }
@@ -71,7 +72,7 @@
 // 生成代码(自定义路径)
 export function genCode(tableName) {
   return request({
-    url: '/tool/gen/genCode/' + tableName,
+    url: '/code/gen/genCode/' + tableName,
     method: 'get'
   })
 }
@@ -79,7 +80,7 @@
 // 同步数据库
 export function synchDb(tableName) {
   return request({
-    url: '/tool/gen/synchDb/' + tableName,
+    url: '/code/gen/synchDb/' + tableName,
     method: 'get'
   })
 }
diff --git a/hugray-admin/src/api/user/account.js b/hugray-admin/src/api/user/account.js
new file mode 100644
index 0000000..bd50035
--- /dev/null
+++ b/hugray-admin/src/api/user/account.js
@@ -0,0 +1,48 @@
+import request from '@/utils/request'
+
+// 注意:本前端对接的是 hugray-server 云网关,会员服务挂在 /account 前缀下
+// (网关 StripPrefix=1 剥掉 /account 后落到 ruoyi-user 服务的 /user/**),
+// 不能照抄 RuoYi 单体版的写法。
+
+// 查询会员列表
+export function listUser(query) {
+  return request({
+    url: '/account/user/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询会员详细
+export function getUser(userId) {
+  return request({
+    url: '/account/user/' + userId,
+    method: 'get'
+  })
+}
+
+// 新增会员
+export function addUser(data) {
+  return request({
+    url: '/account/user',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改会员
+export function updateUser(data) {
+  return request({
+    url: '/account/user',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除会员
+export function delUser(userId) {
+  return request({
+    url: '/account/user/' + userId,
+    method: 'delete'
+  })
+}
diff --git a/hugray-admin/src/store/modules/user.js b/hugray-admin/src/store/modules/user.js
index 66ed920..10691f2 100644
--- a/hugray-admin/src/store/modules/user.js
+++ b/hugray-admin/src/store/modules/user.js
@@ -28,8 +28,8 @@
         const uuid = userInfo.uuid
         return new Promise((resolve, reject) => {
           login(username, password, code, uuid).then(res => {
-            setToken(res.token)
-            this.token = res.token
+            setToken(res.data.access_token)
+            this.token = res.data.access_token
             useLockStore().unlockScreen()
             resolve()
           }).catch(error => {
diff --git a/hugray-admin/src/views/user/account/index.vue b/hugray-admin/src/views/user/account/index.vue
new file mode 100644
index 0000000..3b7104e
--- /dev/null
+++ b/hugray-admin/src/views/user/account/index.vue
@@ -0,0 +1,359 @@
+<template>
+   <div class="app-container">
+      <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
+         <el-form-item label="用户名" prop="userName">
+            <el-input
+               v-model="queryParams.userName"
+               placeholder="请输入用户名"
+               clearable
+               style="width: 200px"
+               @keyup.enter="handleQuery"
+            />
+         </el-form-item>
+         <el-form-item label="手机号" prop="phone">
+            <el-input
+               v-model="queryParams.phone"
+               placeholder="请输入手机号"
+               clearable
+               style="width: 200px"
+               @keyup.enter="handleQuery"
+            />
+         </el-form-item>
+         <el-form-item label="昵称" prop="nickname">
+            <el-input
+               v-model="queryParams.nickname"
+               placeholder="请输入昵称"
+               clearable
+               style="width: 200px"
+               @keyup.enter="handleQuery"
+            />
+         </el-form-item>
+         <el-form-item label="状态" prop="status">
+            <el-select v-model="queryParams.status" placeholder="会员状态" clearable style="width: 200px">
+               <el-option
+                  v-for="dict in sys_normal_disable"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="dict.value"
+               />
+            </el-select>
+         </el-form-item>
+         <el-form-item label="创建时间" style="width: 308px">
+            <el-date-picker
+               v-model="dateRange"
+               value-format="YYYY-MM-DD"
+               type="daterange"
+               range-separator="-"
+               start-placeholder="开始日期"
+               end-placeholder="结束日期"
+            ></el-date-picker>
+         </el-form-item>
+         <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+            <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+         </el-form-item>
+      </el-form>
+
+      <el-row :gutter="10" class="mb8">
+         <el-col :span="1.5">
+            <el-button
+               type="primary"
+               plain
+               icon="Plus"
+               @click="handleAdd"
+               v-hasPermi="['account:user:add']"
+            >新增</el-button>
+         </el-col>
+         <el-col :span="1.5">
+            <el-button
+               type="success"
+               plain
+               icon="Edit"
+               :disabled="single"
+               @click="handleUpdate"
+               v-hasPermi="['account:user:edit']"
+            >修改</el-button>
+         </el-col>
+         <el-col :span="1.5">
+            <el-button
+               type="danger"
+               plain
+               icon="Delete"
+               :disabled="multiple"
+               @click="handleDelete"
+               v-hasPermi="['account:user:remove']"
+            >删除</el-button>
+         </el-col>
+         <el-col :span="1.5">
+            <el-button
+               type="warning"
+               plain
+               icon="Download"
+               @click="handleExport"
+               v-hasPermi="['account:user:export']"
+            >导出</el-button>
+         </el-col>
+         <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+      </el-row>
+
+      <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+         <el-table-column type="selection" width="55" align="center" />
+         <el-table-column label="会员ID" align="center" prop="userId" width="90" />
+         <el-table-column label="用户名" align="center" prop="userName" :show-overflow-tooltip="true" />
+         <el-table-column label="手机号" align="center" prop="phone" width="130" />
+         <el-table-column label="昵称" align="center" prop="nickname" :show-overflow-tooltip="true" />
+         <el-table-column label="性别" align="center" prop="gender" width="80">
+            <template #default="scope">
+               <dict-tag :options="sys_user_sex" :value="scope.row.gender" />
+            </template>
+         </el-table-column>
+         <el-table-column label="状态" align="center" prop="status" width="80">
+            <template #default="scope">
+               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+            </template>
+         </el-table-column>
+         <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+            <template #default="scope">
+               <span>{{ parseTime(scope.row.createTime) }}</span>
+            </template>
+         </el-table-column>
+         <el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
+            <template #default="scope">
+               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['account:user:edit']">修改</el-button>
+               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['account:user:remove']">删除</el-button>
+            </template>
+         </el-table-column>
+      </el-table>
+
+      <pagination
+         v-show="total > 0"
+         :total="total"
+         v-model:page="queryParams.pageNum"
+         v-model:limit="queryParams.pageSize"
+         @pagination="getList"
+      />
+
+      <!-- 添加或修改会员对话框 -->
+      <el-dialog :title="title" v-model="open" width="600px" append-to-body>
+         <el-form ref="userRef" :model="form" :rules="rules" label-width="80px">
+            <el-form-item label="用户名" prop="userName">
+               <el-input v-model="form.userName" placeholder="请输入用户名" maxlength="64" />
+            </el-form-item>
+            <el-form-item label="手机号" prop="phone">
+               <el-input v-model="form.phone" placeholder="请输入手机号" maxlength="20" />
+            </el-form-item>
+            <el-form-item label="密码" prop="password">
+               <el-input
+                  v-model="form.password"
+                  type="password"
+                  show-password
+                  maxlength="20"
+                  :placeholder="form.userId ? '留空则不修改密码' : '请输入密码'"
+               />
+            </el-form-item>
+            <el-form-item label="昵称" prop="nickname">
+               <el-input v-model="form.nickname" placeholder="请输入昵称" maxlength="64" />
+            </el-form-item>
+            <el-form-item label="头像地址" prop="avatar">
+               <el-input v-model="form.avatar" placeholder="请输入头像图片地址" maxlength="255" />
+            </el-form-item>
+            <el-form-item label="性别" prop="gender">
+               <el-radio-group v-model="form.gender">
+                  <el-radio
+                     v-for="dict in sys_user_sex"
+                     :key="dict.value"
+                     :value="dict.value"
+                  >{{ dict.label }}</el-radio>
+               </el-radio-group>
+            </el-form-item>
+            <el-form-item label="生日" prop="birthday">
+               <el-date-picker
+                  v-model="form.birthday"
+                  type="date"
+                  value-format="YYYY-MM-DD"
+                  placeholder="选择日期"
+                  style="width: 100%"
+               />
+            </el-form-item>
+            <el-form-item label="状态" prop="status">
+               <el-radio-group v-model="form.status">
+                  <el-radio
+                     v-for="dict in sys_normal_disable"
+                     :key="dict.value"
+                     :value="dict.value"
+                  >{{ dict.label }}</el-radio>
+               </el-radio-group>
+            </el-form-item>
+            <el-form-item label="备注" prop="remark">
+               <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" maxlength="500" />
+            </el-form-item>
+         </el-form>
+         <template #footer>
+            <div class="dialog-footer">
+               <el-button type="primary" @click="submitForm">确 定</el-button>
+               <el-button @click="cancel">取 消</el-button>
+            </div>
+         </template>
+      </el-dialog>
+   </div>
+</template>
+
+<script setup name="AccountUser">
+import { listUser, addUser, delUser, getUser, updateUser } from "@/api/user/account"
+
+const { proxy } = getCurrentInstance()
+const { sys_user_sex, sys_normal_disable } = useDict("sys_user_sex", "sys_normal_disable")
+
+const userList = ref([])
+const open = ref(false)
+const loading = ref(true)
+const showSearch = ref(true)
+const ids = ref([])
+const single = ref(true)
+const multiple = ref(true)
+const total = ref(0)
+const title = ref("")
+const dateRange = ref([])
+
+const data = reactive({
+  form: {},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    userName: undefined,
+    phone: undefined,
+    nickname: undefined,
+    status: undefined
+  },
+  rules: {
+    userName: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
+    // 新增时密码必填;修改时留空表示不改,故用自定义校验
+    password: [{
+      validator: (rule, value, callback) => {
+        if (!form.value.userId && !value) {
+          callback(new Error("密码不能为空"))
+        } else {
+          callback()
+        }
+      },
+      trigger: "blur"
+    }]
+  }
+})
+
+const { queryParams, form, rules } = toRefs(data)
+
+/** 查询会员列表 */
+function getList() {
+  loading.value = true
+  listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
+    userList.value = response.rows
+    total.value = response.total
+    loading.value = false
+  })
+}
+
+/** 取消按钮 */
+function cancel() {
+  open.value = false
+  reset()
+}
+
+/** 表单重置 */
+function reset() {
+  form.value = {
+    userId: undefined,
+    userName: undefined,
+    phone: undefined,
+    password: undefined,
+    nickname: undefined,
+    avatar: undefined,
+    gender: "2",
+    birthday: undefined,
+    status: "0",
+    remark: undefined
+  }
+  proxy.resetForm("userRef")
+}
+
+/** 搜索按钮操作 */
+function handleQuery() {
+  queryParams.value.pageNum = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+function resetQuery() {
+  dateRange.value = []
+  proxy.resetForm("queryRef")
+  handleQuery()
+}
+
+/** 多选框选中数据 */
+function handleSelectionChange(selection) {
+  ids.value = selection.map(item => item.userId)
+  single.value = selection.length != 1
+  multiple.value = !selection.length
+}
+
+/** 新增按钮操作 */
+function handleAdd() {
+  reset()
+  open.value = true
+  title.value = "添加会员"
+}
+
+/** 修改按钮操作 */
+function handleUpdate(row) {
+  reset()
+  const userId = row.userId || ids.value
+  getUser(userId).then(response => {
+    form.value = response.data
+    // 接口不回传密码,避免把 hash 回填到输入框后又被当成新密码提交
+    form.value.password = undefined
+    open.value = true
+    title.value = "修改会员"
+  })
+}
+
+/** 提交按钮 */
+function submitForm() {
+  proxy.$refs["userRef"].validate(valid => {
+    if (valid) {
+      if (form.value.userId != undefined) {
+        updateUser(form.value).then(() => {
+          proxy.$modal.msgSuccess("修改成功")
+          open.value = false
+          getList()
+        })
+      } else {
+        addUser(form.value).then(() => {
+          proxy.$modal.msgSuccess("新增成功")
+          open.value = false
+          getList()
+        })
+      }
+    }
+  })
+}
+
+/** 删除按钮操作 */
+function handleDelete(row) {
+  const userIds = row.userId || ids.value
+  proxy.$modal.confirm('是否确认删除会员编号为"' + userIds + '"的数据项?').then(function() {
+    return delUser(userIds)
+  }).then(() => {
+    getList()
+    proxy.$modal.msgSuccess("删除成功")
+  }).catch(() => {})
+}
+
+/** 导出按钮操作 */
+function handleExport() {
+  proxy.download("account/user/export", {
+    ...queryParams.value
+  }, `account_user_${new Date().getTime()}.xlsx`)
+}
+
+getList()
+</script>
diff --git a/hugray-admin/vite.config.js b/hugray-admin/vite.config.js
index d3d9b6d..03e6fb5 100644
--- a/hugray-admin/vite.config.js
+++ b/hugray-admin/vite.config.js
@@ -2,7 +2,7 @@
 import path from 'path'
 import createVitePlugins from './vite/plugins'
 
-const baseUrl = 'http://localhost:8080' // 后端接口
+const baseUrl = 'http://localhost:8085' // 后端接口
 
 // https://vitejs.dev/config/
 export default defineConfig(({ mode, command }) => {
diff --git a/hugray-server/bin/start-all.bat b/hugray-server/bin/start-all.bat
new file mode 100644
index 0000000..b7551bf
--- /dev/null
+++ b/hugray-server/bin/start-all.bat
@@ -0,0 +1,148 @@
+@echo off
+setlocal enabledelayedexpansion
+
+rem ============================================================
+rem  Hugray ����һ��������Nacos -> Redis -> 8 ��΢����
+rem  ��ռ�õĶ˿ڻ����������ظ�ִ�У��ݵȣ�
+rem ============================================================
+
+rem ---------------------- ������ ----------------------
+set "JAVA_HOME=D:\jdk-17.0.9"
+set "NACOS_HOME=F:\nacos-3.0.2\nacos"
+set "REDIS_HOME=C:\Users\Admin\Desktop\redis"
+rem ----------------------------------------------------
+
+set "SERVER_DIR=%~dp0.."
+set "LOG_DIR=%SERVER_DIR%\logs"
+set "JAVA=%JAVA_HOME%\bin\java.exe"
+
+if not exist "%JAVA%" (
+    echo [����] δ�ҵ� JDK��%JAVA%
+    echo        ���޸ı��ű������������е� JAVA_HOME
+    pause
+    exit /b 1
+)
+
+cd /d "%SERVER_DIR%"
+if not exist "%LOG_DIR%" md "%LOG_DIR%"
+
+echo.
+echo ============ Hugray ���ؿ����������� ============
+echo  JDK  : %JAVA_HOME%
+echo  ��־ : %LOG_DIR%
+echo ================================================
+echo.
+
+rem ---------- 1. Nacos ----------
+call :port_in_use 8848
+if not errorlevel 1 (
+    echo [����] Nacos ��������
+) else (
+    if not exist "%NACOS_HOME%\bin\startup.cmd" (
+        echo [����] δ�ҵ� Nacos��%NACOS_HOME%\bin\startup.cmd
+        pause
+        exit /b 1
+    )
+    echo [����] Nacos -^> %NACOS_HOME%
+    rem ��Ҫ�� start + startup.cmd��start �����ӽ��̼̳б��ű����ض���� stdin����Ϊ nul����
+    rem startup.cmd ĩβ�� pause ����������ء�����̨��֮�رգ�Nacos �ᱻ����ɱ����
+    rem ���� Start-Process���ӽ����õ���������̨��Nacos ���������뱾�ű����
+    powershell -NoProfile -Command "Start-Process -FilePath '%NACOS_HOME%\bin\startup.cmd' -ArgumentList '-m','standalone' -WindowStyle Hidden"
+)
+
+echo [�ȴ�] Nacos 8848 ���� ...
+call :wait_port 8848 120
+if errorlevel 1 (
+    echo [����] Nacos 120 ����δ��������鿴 %NACOS_HOME%\logs
+    pause
+    exit /b 1
+)
+echo [����] Nacos
+
+rem ---------- 2. Redis ----------
+call :port_in_use 6379
+if not errorlevel 1 (
+    echo [����] Redis ��������
+) else (
+    if not exist "%REDIS_HOME%\redis-server.exe" (
+        echo [����] δ�ҵ� Redis��%REDIS_HOME%\redis-server.exe������
+    ) else (
+        echo [����] Redis -^> %REDIS_HOME%
+        powershell -NoProfile -Command "Start-Process -FilePath '%REDIS_HOME%\redis-server.exe' -ArgumentList '%REDIS_HOME%\redis.windows.conf' -WorkingDirectory '%REDIS_HOME%' -WindowStyle Hidden"
+        call :wait_port 6379 20
+        if errorlevel 1 echo [����] Redis �������������Ժ�ȷ��
+    )
+)
+
+rem ---------- 3. ΢����gateway ����󣬵������������ж���������� ----------
+echo.
+echo ---------- ����΢���� ----------
+call :start_service "ruoyi-auth"    "ruoyi-auth"                 "ruoyi-auth.jar"            9200
+call :start_service "ruoyi-system"  "ruoyi-modules\ruoyi-system" "ruoyi-modules-system.jar"  9201
+call :start_service "ruoyi-gen"     "ruoyi-modules\ruoyi-gen"    "ruoyi-modules-gen.jar"     9202
+call :start_service "ruoyi-job"     "ruoyi-modules\ruoyi-job"    "ruoyi-modules-job.jar"     9203
+call :start_service "ruoyi-file"    "ruoyi-modules\ruoyi-file"   "ruoyi-modules-file.jar"    9300
+call :start_service "ruoyi-user"    "ruoyi-modules\ruoyi-user"  "ruoyi-modules-user.jar"    9210
+call :start_service "ruoyi-monitor" "ruoyi-visual\ruoyi-monitor" "ruoyi-visual-monitor.jar"  9100
+call :start_service "ruoyi-gateway" "ruoyi-gateway"              "ruoyi-gateway.jar"         8085
+
+echo.
+echo ---------- �ȴ����ؾ��� ----------
+call :wait_port 8085 120
+if errorlevel 1 (
+    echo [����] ���� 8085 δ�� 120 ���ھ�������鿴 %LOG_DIR%\ruoyi-gateway.err.log
+) else (
+    echo [����] ���� http://localhost:8085
+)
+
+echo.
+echo ============ ������� ============
+echo  Nacos ����̨ : http://localhost:8082
+echo  ����         : http://localhost:8085
+echo  �����̨     : �� hugray-admin Ŀ¼��npm run dev��
+echo  ֹͣȫ��     : bin\stop-all.bat
+echo ================================
+echo.
+pause
+exit /b 0
+
+
+rem ============================================================
+rem  �ӹ���
+rem ============================================================
+
+rem :port_in_use <�˿�>   -- errorlevel 0 ��ʾ�˿��ѱ�����
+:port_in_use
+netstat -an | findstr /C:":%~1 " | findstr /C:"LISTENING" >nul 2>&1
+exit /b %errorlevel%
+
+rem :wait_port <�˿�> <��ʱ����>  -- errorlevel 0 ��ʾ�Ѿ���
+:wait_port
+set /a _waited=0
+:wait_port_loop
+call :port_in_use %~1
+if not errorlevel 1 exit /b 0
+set /a _waited+=1
+if !_waited! GEQ %~2 exit /b 1
+rem ��ʱ�� ping��timeout �� stdin ���ض���ʱ��ֱ���˳�������ס��
+rem �Ӿ���·����Ϊ�˱��� Git Bash �Ȼ�����ͬ�����߻�� PATH
+"%SystemRoot%\System32\ping.exe" -n 2 -w 1000 127.0.0.1 >nul
+goto :wait_port_loop
+
+rem :start_service <����> <ģ�����Ŀ¼> <jar �ļ���> <�˿�>
+:start_service
+call :port_in_use %~4
+if not errorlevel 1 (
+    echo   [����] %~1 - �˿� %~4 �ѱ�ռ��
+    exit /b 0
+)
+set "_dir=%SERVER_DIR%\%~2\target"
+set "_jar=%_dir%\%~3"
+if not exist "%_jar%" (
+    echo   [����] %~1 - δ�ҵ� %_jar%
+    echo          ����ִ�� bin\package.bat ����
+    exit /b 1
+)
+echo   [����] %~1 - �˿� %~4
+powershell -NoProfile -Command "Start-Process -FilePath '%JAVA%' -ArgumentList '-Dfile.encoding=UTF-8','-Xms512m','-Xmx1024m','-XX:MetaspaceSize=128m','-XX:MaxMetaspaceSize=512m','-jar','%_jar%' -WorkingDirectory '%_dir%' -RedirectStandardOutput '%LOG_DIR%\%~1.out.log' -RedirectStandardError '%LOG_DIR%\%~1.err.log' -WindowStyle Hidden"
+exit /b 0
diff --git a/hugray-server/bin/stop-all.bat b/hugray-server/bin/stop-all.bat
new file mode 100644
index 0000000..83c751d
--- /dev/null
+++ b/hugray-server/bin/stop-all.bat
@@ -0,0 +1,33 @@
+@echo off
+setlocal enabledelayedexpansion
+
+rem ============================================================
+rem  Hugray ����һ��ֹͣ��΢���� + Nacos
+rem  Redis Ϊ��פ�м�������ڱ��ű�ֹͣ��Χ��
+rem ============================================================
+
+echo.
+echo ============ Hugray ���ؿ�������ֹͣ ============
+echo.
+
+rem ��ͣ���أ���ͣҵ��������ͣ Nacos
+for %%p in (8085 9200 9201 9202 9203 9210 9300 9100 8848) do (
+    call :kill_port %%p
+)
+
+echo.
+echo ============ ֹͣ��� ============
+echo  Redis �������У�����ֹͣ���ֶ��������̣�
+echo ================================
+echo.
+pause
+exit /b 0
+
+
+rem :kill_port <�˿�>  -- ���������ö˿ڵĽ���
+:kill_port
+for /f "tokens=5" %%a in ('netstat -ano ^| findstr /C:":%~1 " ^| findstr /C:"LISTENING"') do (
+    echo [ֹͣ] �˿� %~1 - PID %%a
+    taskkill /F /PID %%a >nul 2>&1
+)
+exit /b 0
diff --git a/hugray-server/ruoyi-gateway/src/main/resources/bootstrap.yml b/hugray-server/ruoyi-gateway/src/main/resources/bootstrap.yml
index 23d75b7..514e85e 100644
--- a/hugray-server/ruoyi-gateway/src/main/resources/bootstrap.yml
+++ b/hugray-server/ruoyi-gateway/src/main/resources/bootstrap.yml
@@ -1,6 +1,6 @@
 # Tomcat
 server:
-  port: 8080
+  port: 8085
 
 # Spring
 spring: 
diff --git a/hugray-server/ruoyi-modules/pom.xml b/hugray-server/ruoyi-modules/pom.xml
index 6ac20a6..2aa833b 100644
--- a/hugray-server/ruoyi-modules/pom.xml
+++ b/hugray-server/ruoyi-modules/pom.xml
@@ -13,6 +13,7 @@
         <module>ruoyi-gen</module>
         <module>ruoyi-job</module>
         <module>ruoyi-file</module>
+        <module>ruoyi-user</module>
     </modules>
 
     <artifactId>ruoyi-modules</artifactId>
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/pom.xml b/hugray-server/ruoyi-modules/ruoyi-user/pom.xml
new file mode 100644
index 0000000..69f1416
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.ruoyi</groupId>
+        <artifactId>ruoyi-modules</artifactId>
+        <version>3.6.8</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-modules-user</artifactId>
+
+    <description>
+        ruoyi-modules-user用户服务(C端会员)
+    </description>
+
+    <dependencies>
+
+        <!-- SpringCloud Alibaba Nacos -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <!-- SpringCloud Alibaba Nacos Config -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <!-- SpringCloud Alibaba Sentinel -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+        </dependency>
+
+        <!-- SpringBoot Actuator -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!-- Mysql Connector -->
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+        </dependency>
+
+        <!-- RuoYi Common DataSource -->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common-datasource</artifactId>
+        </dependency>
+
+        <!-- RuoYi Common DataScope -->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common-datascope</artifactId>
+        </dependency>
+
+        <!-- RuoYi Common Log -->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common-log</artifactId>
+        </dependency>
+
+        <!-- RuoYi Common Swagger -->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common-swagger</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <!-- finalName 固定为 artifactId,jar 名必须与 bin/start-all.bat 里的写法一致 -->
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/RuoYiUserApplication.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/RuoYiUserApplication.java
new file mode 100644
index 0000000..f9eaf3a
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/RuoYiUserApplication.java
@@ -0,0 +1,32 @@
+package com.ruoyi.user;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import com.ruoyi.common.security.annotation.EnableCustomConfig;
+import com.ruoyi.common.security.annotation.EnableRyFeignClients;
+
+/**
+ * 用户服务(C端会员)
+ *
+ * @author ruoyi
+ */
+@EnableCustomConfig
+@EnableRyFeignClients
+@SpringBootApplication
+public class RuoYiUserApplication
+{
+    public static void main(String[] args)
+    {
+        SpringApplication.run(RuoYiUserApplication.class, args);
+        System.out.println("(♥◠‿◠)ノ゙  用户服务启动成功   ლ(´ڡ`ლ)゙  \n" +
+                " .-------.       ____     __        \n" +
+                " |  _ _   \\      \\   \\   /  /    \n" +
+                " | ( ' )  |       \\  _. /  '       \n" +
+                " |(_ o _) /        _( )_ .'         \n" +
+                " | (_,_).' __  ___(_ o _)'          \n" +
+                " |  |\\ \\  |  ||   |(_,_)'         \n" +
+                " |  | \\ `'   /|   `-'  /           \n" +
+                " |  |  \\    /  \\      /           \n" +
+                " ''-'   `'-'    `-..-'              ");
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java
new file mode 100644
index 0000000..ba90416
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java
@@ -0,0 +1,131 @@
+package com.ruoyi.user.controller;
+
+import java.util.List;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+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.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.annotation.RequiresPermissions;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.user.domain.AccountUser;
+import com.ruoyi.user.service.IAccountUserService;
+
+/**
+ * 会员用户 信息操作处理
+ *
+ * 网关前缀 /account 由 StripPrefix=1 剥掉,故此处映射为 /user,
+ * 外部实际路径是 /account/user/**。
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/user")
+public class AccountUserController extends BaseController
+{
+    @Autowired
+    private IAccountUserService accountUserService;
+
+    /**
+     * 查询会员列表
+     */
+    @RequiresPermissions("account:user:list")
+    @GetMapping("/list")
+    public TableDataInfo list(AccountUser accountUser)
+    {
+        startPage();
+        List<AccountUser> list = accountUserService.selectAccountUserList(accountUser);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出会员列表
+     */
+    @RequiresPermissions("account:user:export")
+    @Log(title = "会员管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, AccountUser accountUser)
+    {
+        List<AccountUser> list = accountUserService.selectAccountUserList(accountUser);
+        ExcelUtil<AccountUser> util = new ExcelUtil<AccountUser>(AccountUser.class);
+        util.exportExcel(response, list, "会员数据");
+    }
+
+    /**
+     * 获取会员详细信息
+     */
+    @RequiresPermissions("account:user:query")
+    @GetMapping(value = "/{userId}")
+    public AjaxResult getInfo(@PathVariable Long userId)
+    {
+        return success(accountUserService.selectAccountUserById(userId));
+    }
+
+    /**
+     * 新增会员
+     */
+    @RequiresPermissions("account:user:add")
+    @Log(title = "会员管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody AccountUser accountUser)
+    {
+        if (!accountUserService.checkUsernameUnique(accountUser))
+        {
+            return error("新增会员'" + accountUser.getUserName() + "'失败,用户名已存在");
+        }
+        if (!accountUserService.checkPhoneUnique(accountUser))
+        {
+            return error("新增会员'" + accountUser.getUserName() + "'失败,手机号已存在");
+        }
+        if (StringUtils.isEmpty(accountUser.getPassword()))
+        {
+            return error("密码不能为空");
+        }
+        accountUser.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(accountUserService.insertAccountUser(accountUser));
+    }
+
+    /**
+     * 修改会员
+     */
+    @RequiresPermissions("account:user:edit")
+    @Log(title = "会员管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody AccountUser accountUser)
+    {
+        if (!accountUserService.checkUsernameUnique(accountUser))
+        {
+            return error("修改会员'" + accountUser.getUserName() + "'失败,用户名已存在");
+        }
+        if (!accountUserService.checkPhoneUnique(accountUser))
+        {
+            return error("修改会员'" + accountUser.getUserName() + "'失败,手机号已存在");
+        }
+        accountUser.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(accountUserService.updateAccountUser(accountUser));
+    }
+
+    /**
+     * 删除会员
+     */
+    @RequiresPermissions("account:user:remove")
+    @Log(title = "会员管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{userIds}")
+    public AjaxResult remove(@PathVariable Long[] userIds)
+    {
+        return toAjax(accountUserService.deleteAccountUserByIds(userIds));
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java
new file mode 100644
index 0000000..46012b5
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java
@@ -0,0 +1,193 @@
+package com.ruoyi.user.domain;
+
+import java.util.Date;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * C端会员用户表 account_user
+ *
+ * @author ruoyi
+ */
+public class AccountUser extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 会员ID */
+    @Excel(name = "会员ID", cellType = ColumnType.NUMERIC)
+    private Long userId;
+
+    /**
+     * 用户名
+     *
+     * 注意:属性名刻意用驼峰 userName,与上游 SysUser.userName 一致。网关的 AuthFilter 会
+     * 给每个转发请求加上请求头 username=<登录名>(SecurityConstants.DETAILS_USERNAME),
+     * 而 Spring 6+ 的 @ModelAttribute 会把请求头一并绑定到命令对象——若此处叫 username,
+     * 列表/导出接口在未显式传参时会被注入当前登录名,导致查询结果被静默过滤(实测列表恒为空)。
+     */
+    @Excel(name = "用户名")
+    private String userName;
+
+    /** 手机号 */
+    @Excel(name = "手机号")
+    private String phone;
+
+    /** 密码(BCrypt 存储)。只入不出,避免密码随响应回传 */
+    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+    private String password;
+
+    /** 昵称 */
+    @Excel(name = "昵称")
+    private String nickname;
+
+    /** 头像地址 */
+    private String avatar;
+
+    /** 性别(0男 1女 2未知,对齐 sys_user_sex 字典) */
+    @Excel(name = "性别", readConverterExp = "0=男,1=女,2=未知")
+    private String gender;
+
+    /** 生日 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "生日", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date birthday;
+
+    /** 状态(0正常 1停用) */
+    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /** 删除标志(0存在 2删除) */
+    private String delFlag;
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    @NotBlank(message = "用户名不能为空")
+    @Size(min = 0, max = 64, message = "用户名长度不能超过64个字符")
+    public String getUserName()
+    {
+        return userName;
+    }
+
+    public void setUserName(String userName)
+    {
+        this.userName = userName;
+    }
+
+    @Size(min = 0, max = 20, message = "手机号长度不能超过20个字符")
+    public String getPhone()
+    {
+        return phone;
+    }
+
+    public void setPhone(String phone)
+    {
+        this.phone = phone;
+    }
+
+    @Size(min = 0, max = 20, message = "密码长度必须在5到20个字符之间")
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    @Size(min = 0, max = 64, message = "昵称长度不能超过64个字符")
+    public String getNickname()
+    {
+        return nickname;
+    }
+
+    public void setNickname(String nickname)
+    {
+        this.nickname = nickname;
+    }
+
+    public String getAvatar()
+    {
+        return avatar;
+    }
+
+    public void setAvatar(String avatar)
+    {
+        this.avatar = avatar;
+    }
+
+    public String getGender()
+    {
+        return gender;
+    }
+
+    public void setGender(String gender)
+    {
+        this.gender = gender;
+    }
+
+    public Date getBirthday()
+    {
+        return birthday;
+    }
+
+    public void setBirthday(Date birthday)
+    {
+        this.birthday = birthday;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public String getDelFlag()
+    {
+        return delFlag;
+    }
+
+    public void setDelFlag(String delFlag)
+    {
+        this.delFlag = delFlag;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("userId", getUserId())
+            .append("userName", getUserName())
+            .append("phone", getPhone())
+            .append("nickname", getNickname())
+            .append("avatar", getAvatar())
+            .append("gender", getGender())
+            .append("birthday", getBirthday())
+            .append("status", getStatus())
+            .append("delFlag", getDelFlag())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java
new file mode 100644
index 0000000..3e88450
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java
@@ -0,0 +1,52 @@
+package com.ruoyi.user.mapper;
+
+import java.util.List;
+import com.ruoyi.user.domain.AccountUser;
+
+/**
+ * 会员用户 数据层
+ *
+ * @author ruoyi
+ */
+public interface AccountUserMapper
+{
+    /**
+     * 根据会员ID查询
+     */
+    public AccountUser selectAccountUserById(Long userId);
+
+    /**
+     * 根据用户名查询(查重用)
+     */
+    public AccountUser selectAccountUserByUsername(String userName);
+
+    /**
+     * 根据手机号查询(查重用)
+     */
+    public AccountUser selectAccountUserByPhone(String phone);
+
+    /**
+     * 查询会员列表
+     */
+    public List<AccountUser> selectAccountUserList(AccountUser accountUser);
+
+    /**
+     * 新增会员
+     */
+    public int insertAccountUser(AccountUser accountUser);
+
+    /**
+     * 修改会员
+     */
+    public int updateAccountUser(AccountUser accountUser);
+
+    /**
+     * 逻辑删除会员
+     */
+    public int deleteAccountUserById(Long userId);
+
+    /**
+     * 批量逻辑删除会员
+     */
+    public int deleteAccountUserByIds(Long[] userIds);
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java
new file mode 100644
index 0000000..c18b9e3
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java
@@ -0,0 +1,49 @@
+package com.ruoyi.user.service;
+
+import java.util.List;
+import com.ruoyi.user.domain.AccountUser;
+
+/**
+ * 会员用户 业务层
+ *
+ * @author ruoyi
+ */
+public interface IAccountUserService
+{
+    /**
+     * 根据会员ID查询
+     */
+    public AccountUser selectAccountUserById(Long userId);
+
+    /**
+     * 查询会员列表
+     */
+    public List<AccountUser> selectAccountUserList(AccountUser accountUser);
+
+    /**
+     * 校验用户名是否唯一
+     *
+     * @return true 唯一,false 已存在
+     */
+    public boolean checkUsernameUnique(AccountUser accountUser);
+
+    /**
+     * 校验手机号是否唯一
+     */
+    public boolean checkPhoneUnique(AccountUser accountUser);
+
+    /**
+     * 新增会员(密码加密后落库)
+     */
+    public int insertAccountUser(AccountUser accountUser);
+
+    /**
+     * 修改会员(密码为空则不改密码)
+     */
+    public int updateAccountUser(AccountUser accountUser);
+
+    /**
+     * 批量逻辑删除会员
+     */
+    public int deleteAccountUserByIds(Long[] userIds);
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java
new file mode 100644
index 0000000..08ce572
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java
@@ -0,0 +1,88 @@
+package com.ruoyi.user.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.user.domain.AccountUser;
+import com.ruoyi.user.mapper.AccountUserMapper;
+import com.ruoyi.user.service.IAccountUserService;
+
+/**
+ * 会员用户 业务层实现
+ *
+ * @author ruoyi
+ */
+@Service
+public class AccountUserServiceImpl implements IAccountUserService
+{
+    @Autowired
+    private AccountUserMapper accountUserMapper;
+
+    @Override
+    public AccountUser selectAccountUserById(Long userId)
+    {
+        return accountUserMapper.selectAccountUserById(userId);
+    }
+
+    @Override
+    public List<AccountUser> selectAccountUserList(AccountUser accountUser)
+    {
+        return accountUserMapper.selectAccountUserList(accountUser);
+    }
+
+    @Override
+    public boolean checkUsernameUnique(AccountUser accountUser)
+    {
+        Long userId = StringUtils.isNull(accountUser.getUserId()) ? -1L : accountUser.getUserId();
+        AccountUser info = accountUserMapper.selectAccountUserByUsername(accountUser.getUserName());
+        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    @Override
+    public boolean checkPhoneUnique(AccountUser accountUser)
+    {
+        // 手机号非必填,为空时不做唯一性校验,否则会与库里其它空手机号记录相撞
+        if (StringUtils.isEmpty(accountUser.getPhone()))
+        {
+            return UserConstants.UNIQUE;
+        }
+        Long userId = StringUtils.isNull(accountUser.getUserId()) ? -1L : accountUser.getUserId();
+        AccountUser info = accountUserMapper.selectAccountUserByPhone(accountUser.getPhone());
+        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    @Override
+    public int insertAccountUser(AccountUser accountUser)
+    {
+        accountUser.setPassword(SecurityUtils.encryptPassword(accountUser.getPassword()));
+        return accountUserMapper.insertAccountUser(accountUser);
+    }
+
+    @Override
+    public int updateAccountUser(AccountUser accountUser)
+    {
+        // 不为空才加密;为空时保持 null,mapper 的 <if> 会跳过该列,原密码不受影响
+        if (StringUtils.isNotEmpty(accountUser.getPassword()))
+        {
+            accountUser.setPassword(SecurityUtils.encryptPassword(accountUser.getPassword()));
+        }
+        return accountUserMapper.updateAccountUser(accountUser);
+    }
+
+    @Override
+    public int deleteAccountUserByIds(Long[] userIds)
+    {
+        return accountUserMapper.deleteAccountUserByIds(userIds);
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/banner.txt b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/banner.txt
new file mode 100644
index 0000000..f2d9f2d
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+                            _
+                           (_)
+ _ __  _   _   ___   _   _  _  ______   __ _   ___  _ __
+| '__|| | | | / _ \ | | | || ||______| / _` | / _ \| '_ \
+| |   | |_| || (_) || |_| || |        | (_| ||  __/| | | |
+|_|    \__,_| \___/  \__, ||_|         \__, | \___||_| |_|
+                      __/ |             __/ |
+                     |___/             |___/
\ No newline at end of file
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/bootstrap.yml b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..9844a87
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/bootstrap.yml
@@ -0,0 +1,27 @@
+# Tomcat
+server:
+  port: 9210
+
+# Spring
+spring:
+  application:
+    # 应用名称
+    name: ruoyi-user
+  profiles:
+    # 环境配置
+    active: dev
+  cloud:
+    nacos:
+      discovery:
+        # 服务注册地址
+        server-addr: 127.0.0.1:8848
+      config:
+        # 配置中心地址
+        server-addr: 127.0.0.1:8848
+  config:
+    # 配置文件格式
+    file-extension: yml
+    # 数据源、mybatis、springdoc 全在 Nacos,此处只声明要加载哪两份配置
+    import:
+      - nacos:application-${spring.profiles.active}.${spring.config.file-extension}
+      - nacos:${spring.application.name}-${spring.profiles.active}.${spring.config.file-extension}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/logback.xml b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/logback.xml
new file mode 100644
index 0000000..f077d9f
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/logback.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="60 seconds" debug="false">
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="logs/ruoyi-user" />
+   <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+    <!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+    <!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 60天 -->
+			<maxHistory>60</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 60天 -->
+			<maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 系统模块日志级别控制  -->
+	<logger name="com.ruoyi" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+</configuration>
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml
new file mode 100644
index 0000000..e7c989d
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml
@@ -0,0 +1,122 @@
+<?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="delFlag"    column="del_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>
+
+    <sql id="selectAccountUserVo">
+        select user_id, username, phone, password, nickname, avatar, gender, birthday, status, del_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="userName != null and userName != ''"> and username like concat('%', #{userName}, '%')</if>
+            <if test="phone != null and phone != ''"> and phone like concat('%', #{phone}, '%')</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="params.beginTime != null and params.beginTime != ''">
+                and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
+            </if>
+            <if test="params.endTime != null and params.endTime != ''">
+                and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
+            </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>
+
+    <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="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="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="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>
+
+    <!-- 逻辑删除:保留行,仅置删除标志 -->
+    <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>
+
+</mapper>
diff --git a/hugray-server/sql/hugray_account_20261009.sql b/hugray-server/sql/hugray_account_20261009.sql
new file mode 100644
index 0000000..d63deb7
--- /dev/null
+++ b/hugray-server/sql/hugray_account_20261009.sql
@@ -0,0 +1,34 @@
+-- ----------------------------
+-- C 端会员用户表(ruoyi-user 用户服务)
+--
+-- 目标库 hugray-account 由使用方预先创建(utf8mb4)。
+-- 注意:库名含连字符,任何引用都必须用反引号。
+-- ----------------------------
+
+USE `hugray-account`;
+
+DROP TABLE IF EXISTS `account_user`;
+CREATE TABLE `account_user` (
+  `user_id`     bigint(20)   NOT NULL AUTO_INCREMENT COMMENT '会员ID',
+  `username`    varchar(64)  NOT NULL                COMMENT '用户名',
+  `phone`       varchar(20)  DEFAULT NULL            COMMENT '手机号',
+  `password`    varchar(100) DEFAULT NULL            COMMENT '密码(BCrypt)',
+  `nickname`    varchar(64)  DEFAULT NULL            COMMENT '昵称',
+  `avatar`      varchar(255) DEFAULT NULL            COMMENT '头像地址',
+  `gender`      char(1)      DEFAULT '2'             COMMENT '性别(0男 1女 2未知,对齐 sys_user_sex 字典)',
+  `birthday`    date         DEFAULT NULL            COMMENT '生日',
+  `status`      char(1)      DEFAULT '0'             COMMENT '状态(0正常 1停用)',
+  `del_flag`    char(1)      DEFAULT '0'             COMMENT '删除标志(0存在 2删除)',
+  `create_by`   varchar(64)  DEFAULT ''              COMMENT '创建者',
+  `create_time` datetime     DEFAULT NULL            COMMENT '创建时间',
+  `update_by`   varchar(64)  DEFAULT ''              COMMENT '更新者',
+  `update_time` datetime     DEFAULT NULL            COMMENT '更新时间',
+  `remark`      varchar(500) DEFAULT NULL            COMMENT '备注',
+  PRIMARY KEY (`user_id`),
+  -- 刻意不加 username 唯一索引:删除是逻辑删除(del_flag=2),残留行会永久占用用户名,
+  -- 与「服务层只校验 del_flag='0' 的记录」冲突,导致新增时报 500。此处对齐上游
+  -- sys_user 的做法(ry_20260417.sql 中 sys_user 仅有主键),唯一性由
+  -- AccountUserServiceImpl.checkUsernameUnique 单一权威保证。
+  KEY `idx_account_user_username` (`username`),
+  KEY `idx_account_user_phone` (`phone`)
+) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COMMENT='C端会员用户表';
diff --git a/hugray-server/sql/hugray_menu_account_20261009.sql b/hugray-server/sql/hugray_menu_account_20261009.sql
new file mode 100644
index 0000000..40916c0
--- /dev/null
+++ b/hugray-server/sql/hugray_menu_account_20261009.sql
@@ -0,0 +1,26 @@
+-- ----------------------------
+-- 用户服务(C 端会员)菜单
+--
+-- 目标库为业务库 hugray(与 sys_menu 同库)。
+-- 号段取 2100~2106:现有最大 menu_id 为 1060,此段与之不冲突。
+--
+-- 不需要写 sys_role_menu:超级管理员在 SysMenuServiceImpl 里走
+-- selectMenuTreeAll() / selectMenuList() 全量返回,与角色菜单表无关。
+-- 若要授权给非 admin 角色,再自行插 sys_role_menu。
+-- ----------------------------
+
+USE `hugray`;
+
+-- 可重复执行:先清掉本号段,再插入
+DELETE FROM `sys_menu` WHERE `menu_id` BETWEEN 2100 AND 2106;
+
+INSERT INTO `sys_menu`
+(`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `route_name`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`)
+VALUES
+(2100, '用户服务', 0,    5, 'user',    NULL,                   NULL, '', 1, 0, 'M', '0', '0', '',                   'user',    'admin', sysdate(), '', NULL, '用户服务目录'),
+(2101, '会员管理', 2100, 1, 'account', 'user/account/index',   NULL, '', 1, 0, 'C', '0', '0', 'account:user:list',  'peoples', 'admin', sysdate(), '', NULL, '会员管理菜单'),
+(2102, '会员查询', 2101, 1, '',        NULL,                   NULL, '', 1, 0, 'F', '0', '0', 'account:user:query', '#',       'admin', sysdate(), '', NULL, ''),
+(2103, '会员新增', 2101, 2, '',        NULL,                   NULL, '', 1, 0, 'F', '0', '0', 'account:user:add',   '#',       'admin', sysdate(), '', NULL, ''),
+(2104, '会员修改', 2101, 3, '',        NULL,                   NULL, '', 1, 0, 'F', '0', '0', 'account:user:edit',  '#',       'admin', sysdate(), '', NULL, ''),
+(2105, '会员删除', 2101, 4, '',        NULL,                   NULL, '', 1, 0, 'F', '0', '0', 'account:user:remove','#',       'admin', sysdate(), '', NULL, ''),
+(2106, '会员导出', 2101, 5, '',        NULL,                   NULL, '', 1, 0, 'F', '0', '0', 'account:user:export','#',       'admin', sysdate(), '', NULL, '');

--
Gitblit v1.7.1