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') >= date_format(#{params.beginTime},'%y%m%d')
+ </if>
+ <if test="params.endTime != null and params.endTime != ''">
+ and date_format(create_time,'%y%m%d') <= 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