ソースを参照

revert: 恢复master到合并前状态

jiapu 5 ヶ月 前
コミット
f7423f9b36
100 ファイル変更324 行追加7696 行削除
  1. 18 6
      .gitignore
  2. 306 60
      AGENTS.md
  3. 0 0
      README.md
  4. 0 23
      code/.gitignore
  5. 0 306
      code/AGENTS.md
  6. 0 31
      code/backend/src/main/java/com/aijiuyi/admin/common/annotation/Log.java
  7. 0 14
      code/backend/src/main/java/com/aijiuyi/admin/common/annotation/NoAuth.java
  8. 0 176
      code/backend/src/main/java/com/aijiuyi/admin/common/aspect/LogAspect.java
  9. 0 105
      code/backend/src/main/java/com/aijiuyi/admin/common/config/AcupointSchemaInitializer.java
  10. 0 39
      code/backend/src/main/java/com/aijiuyi/admin/common/config/AsyncConfig.java
  11. 0 29
      code/backend/src/main/java/com/aijiuyi/admin/common/config/AuthProperties.java
  12. 0 29
      code/backend/src/main/java/com/aijiuyi/admin/common/config/MybatisPlusConfig.java
  13. 0 207
      code/backend/src/main/java/com/aijiuyi/admin/common/config/PlanDataInitializer.java
  14. 0 55
      code/backend/src/main/java/com/aijiuyi/admin/common/config/RedisConfig.java
  15. 0 47
      code/backend/src/main/java/com/aijiuyi/admin/common/config/WebMvcConfig.java
  16. 0 145
      code/backend/src/main/java/com/aijiuyi/admin/common/constant/ResultCode.java
  17. 0 108
      code/backend/src/main/java/com/aijiuyi/admin/common/context/RequestContext.java
  18. 0 89
      code/backend/src/main/java/com/aijiuyi/admin/common/entity/Result.java
  19. 0 57
      code/backend/src/main/java/com/aijiuyi/admin/common/entity/SysLog.java
  20. 0 39
      code/backend/src/main/java/com/aijiuyi/admin/common/enums/OperationType.java
  21. 0 47
      code/backend/src/main/java/com/aijiuyi/admin/common/exception/BusinessException.java
  22. 0 91
      code/backend/src/main/java/com/aijiuyi/admin/common/exception/GlobalExceptionHandler.java
  23. 0 42
      code/backend/src/main/java/com/aijiuyi/admin/common/handler/MyMetaObjectHandler.java
  24. 0 162
      code/backend/src/main/java/com/aijiuyi/admin/common/interceptor/TokenInterceptor.java
  25. 0 47
      code/backend/src/main/java/com/aijiuyi/admin/common/service/SysLogService.java
  26. 0 102
      code/backend/src/main/java/com/aijiuyi/admin/common/service/impl/SysLogServiceImpl.java
  27. 0 275
      code/backend/src/main/java/com/aijiuyi/admin/common/util/AcupointLocationParser.java
  28. 0 216
      code/backend/src/main/java/com/aijiuyi/admin/common/util/AcupointYReference.java
  29. 0 57
      code/backend/src/main/java/com/aijiuyi/admin/common/util/IpUtil.java
  30. 0 97
      code/backend/src/main/java/com/aijiuyi/admin/common/util/LogUtil.java
  31. 0 327
      code/backend/src/main/java/com/aijiuyi/admin/common/util/RedisUtil.java
  32. 0 133
      code/backend/src/main/java/com/aijiuyi/admin/common/util/TokenUtil.java
  33. 0 144
      code/backend/src/main/java/com/aijiuyi/admin/controller/AcupointController.java
  34. 0 147
      code/backend/src/main/java/com/aijiuyi/admin/controller/AdminController.java
  35. 0 122
      code/backend/src/main/java/com/aijiuyi/admin/controller/AppUserController.java
  36. 0 91
      code/backend/src/main/java/com/aijiuyi/admin/controller/AuthController.java
  37. 0 186
      code/backend/src/main/java/com/aijiuyi/admin/controller/DeviceController.java
  38. 0 119
      code/backend/src/main/java/com/aijiuyi/admin/controller/MoxibustionTechniqueController.java
  39. 0 154
      code/backend/src/main/java/com/aijiuyi/admin/controller/PlanController.java
  40. 0 36
      code/backend/src/main/java/com/aijiuyi/admin/controller/SimulationController.java
  41. 0 63
      code/backend/src/main/java/com/aijiuyi/admin/controller/SysLogController.java
  42. 0 48
      code/backend/src/main/java/com/aijiuyi/admin/controller/UserAcupointController.java
  43. 0 115
      code/backend/src/main/java/com/aijiuyi/admin/controller/UserDeviceController.java
  44. 0 51
      code/backend/src/main/java/com/aijiuyi/admin/controller/UserPlanController.java
  45. 0 140
      code/backend/src/main/java/com/aijiuyi/admin/controller/UserProfileController.java
  46. 0 28
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/AcupointQueryDTO.java
  47. 0 28
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/AdminQueryDTO.java
  48. 0 28
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/AppUserQueryDTO.java
  49. 0 31
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/DeviceQueryDTO.java
  50. 0 39
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/DeviceSaveDTO.java
  51. 0 20
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/LoginDTO.java
  52. 0 25
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/MoxibustionTechniqueQueryDTO.java
  53. 0 37
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/PlanQueryDTO.java
  54. 0 58
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/PlanSaveDTO.java
  55. 0 63
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/SimulationResponse.java
  56. 0 32
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/SysLogQueryDTO.java
  57. 0 28
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/UserDeviceQueryDTO.java
  58. 0 19
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/UserPlanQueryDTO.java
  59. 0 68
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/UserProfileDTO.java
  60. 0 34
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/UserProfileQueryDTO.java
  61. 0 27
      code/backend/src/main/java/com/aijiuyi/admin/controller/dto/YReferenceOptionDTO.java
  62. 0 126
      code/backend/src/main/java/com/aijiuyi/admin/entity/Acupoint.java
  63. 0 41
      code/backend/src/main/java/com/aijiuyi/admin/entity/AdminPermission.java
  64. 0 65
      code/backend/src/main/java/com/aijiuyi/admin/entity/AppUser.java
  65. 0 70
      code/backend/src/main/java/com/aijiuyi/admin/entity/Device.java
  66. 0 31
      code/backend/src/main/java/com/aijiuyi/admin/entity/DeviceUserVO.java
  67. 0 90
      code/backend/src/main/java/com/aijiuyi/admin/entity/MoxibustionTechnique.java
  68. 0 90
      code/backend/src/main/java/com/aijiuyi/admin/entity/Plan.java
  69. 0 64
      code/backend/src/main/java/com/aijiuyi/admin/entity/PlanStep.java
  70. 0 52
      code/backend/src/main/java/com/aijiuyi/admin/entity/SysUser.java
  71. 0 62
      code/backend/src/main/java/com/aijiuyi/admin/entity/UserAcupoint.java
  72. 0 55
      code/backend/src/main/java/com/aijiuyi/admin/entity/UserDevice.java
  73. 0 61
      code/backend/src/main/java/com/aijiuyi/admin/entity/UserPlan.java
  74. 0 81
      code/backend/src/main/java/com/aijiuyi/admin/entity/UserProfile.java
  75. 0 12
      code/backend/src/main/java/com/aijiuyi/admin/mapper/AdminPermissionMapper.java
  76. 0 36
      code/backend/src/main/java/com/aijiuyi/admin/mapper/DeviceMapper.java
  77. 0 12
      code/backend/src/main/java/com/aijiuyi/admin/mapper/MoxibustionTechniqueMapper.java
  78. 0 12
      code/backend/src/main/java/com/aijiuyi/admin/mapper/PlanMapper.java
  79. 0 12
      code/backend/src/main/java/com/aijiuyi/admin/mapper/PlanStepMapper.java
  80. 0 36
      code/backend/src/main/java/com/aijiuyi/admin/mapper/SysLogMapper.java
  81. 0 13
      code/backend/src/main/java/com/aijiuyi/admin/mapper/SysUserMapper.java
  82. 0 38
      code/backend/src/main/java/com/aijiuyi/admin/mapper/UserAcupointMapper.java
  83. 0 12
      code/backend/src/main/java/com/aijiuyi/admin/mapper/UserDeviceMapper.java
  84. 0 22
      code/backend/src/main/java/com/aijiuyi/admin/mapper/UserPlanMapper.java
  85. 0 26
      code/backend/src/main/java/com/aijiuyi/admin/mapper/UserProfileMapper.java
  86. 0 65
      code/backend/src/main/java/com/aijiuyi/admin/service/AcupointService.java
  87. 0 83
      code/backend/src/main/java/com/aijiuyi/admin/service/AdminService.java
  88. 0 71
      code/backend/src/main/java/com/aijiuyi/admin/service/AppUserService.java
  89. 0 99
      code/backend/src/main/java/com/aijiuyi/admin/service/DeviceService.java
  90. 0 50
      code/backend/src/main/java/com/aijiuyi/admin/service/MoxibustionTechniqueService.java
  91. 0 96
      code/backend/src/main/java/com/aijiuyi/admin/service/PlanService.java
  92. 0 19
      code/backend/src/main/java/com/aijiuyi/admin/service/SimulationService.java
  93. 0 31
      code/backend/src/main/java/com/aijiuyi/admin/service/SysUserService.java
  94. 0 37
      code/backend/src/main/java/com/aijiuyi/admin/service/UserAcupointService.java
  95. 0 59
      code/backend/src/main/java/com/aijiuyi/admin/service/UserDeviceService.java
  96. 0 33
      code/backend/src/main/java/com/aijiuyi/admin/service/UserPlanService.java
  97. 0 67
      code/backend/src/main/java/com/aijiuyi/admin/service/UserProfileService.java
  98. 0 217
      code/backend/src/main/java/com/aijiuyi/admin/service/impl/AcupointServiceImpl.java
  99. 0 236
      code/backend/src/main/java/com/aijiuyi/admin/service/impl/AdminServiceImpl.java
  100. 0 272
      code/backend/src/main/java/com/aijiuyi/admin/service/impl/AppUserServiceImpl.java

+ 18 - 6
.gitignore

@@ -1,10 +1,22 @@
+# Java / Maven
+target/
+*.class
+*.jar
+*.war
+*.ear
+
 # IDE
-.cursor/
 .idea/
+*.iml
+.classpath
+.project
+.settings/
+.vscode/
 
-# Local logs
-logs/
-code/backend/logs/
+# Node.js
+node_modules/
+dist/
 
-# Frontend cache
-code/frontend/.vite/
+# OS
+.DS_Store
+Thumbs.db

+ 306 - 60
AGENTS.md

@@ -1,60 +1,306 @@
-# 项目协作规则
-
-## 基本原则
-
-- 修改代码前先阅读相关文件,遵循现有项目结构、命名和实现风格。
-- 保持改动聚焦,只处理当前任务相关内容,不做无关重构。
-- 不要覆盖或回滚用户已有改动,除非用户明确要求。
-- 不要提交日志、临时文件、构建产物或本地环境配置。
-
-## 运行与构建
-
-- 前端构建优先使用:
-  `npm run build`
-- 如果需要运行前端开发服务,先查看现有脚本和端口配置,再启动对应命令。
-- 如果需要运行后端服务或测试,先查看后端目录中的 Maven/Gradle 配置,再选择合适命令。
-- 运行命令失败时,需要说明失败原因和已尝试的验证方式。
-
-## 修改限制
-
-- 不要修改以下本地配置文件,除非用户明确要求:
-  `code/backend/src/main/resources/application-dev.yml`
-- 不要提交以下内容:
-  `code/backend/logs/**`
-  `logs/**`
-- 涉及用户数据、方案、穴位、经络、调理内容时,要优先保证已有数据兼容性。
-- 涉及中医术语、穴位定位和方案描述时,要保持表述准确、谨慎。
-
-## 测试与验证
-
-- 前端代码改动后,优先运行:
-  `npm run build`
-- 后端代码改动后,优先运行相关单元测试或项目已有验证命令。
-- 如果改动范围较小且没有合适测试,也需要进行静态检查或说明未运行测试的原因。
-
-## Git 规则
-
-- 不要自动执行 `git push`,除非用户明确要求。
-- 不要使用破坏性 Git 命令,例如 `git reset --hard`、`git checkout -- <file>`,除非用户明确要求。
-- 提交前检查暂存内容,避免把本地配置、日志或无关文件加入提交。
-- 如果用户要求提交,提交信息优先使用中文,并简洁描述本次改动。
-- 不要将.cursor文件夹和.idea文件夹提交到git
-- Git 仓库: http://git.iwintrue.com/liaoxg/ajy
-- 同事分支:liyu_2026_04_07
-- 我的分支:jiapu_aijiuyi
-- 项目路径(仓库内): code/aijiuyi
-- Git 用户名: jiapu
-- 分支: jiapu_aijiuyi
-- 拉取代码规则:将同事分支的openspec文件夹下的所有文件(不包括openspec目录)同步到我的code目录下。
-- git提交日志:在每次提交的时候要询问我,如果我选择空白,则你可以自己总结边写日志
-- 提交代码规则:application-dev.yml文件默认不参与git提交。
-
-## 回复要求
-
-- 完成修改后,简要说明改了哪些文件、做了什么验证。
-- 如果有未完成事项、测试无法运行或需要用户决策的地方,需要明确说明。
-- 语言:和我对话,思考的时候使用中文。
-
-## 项目信息
-- 项目名称: aijiuyi
-- 可以读取并使用application.yml、application-dev.yml中的datasource属性
+# Agent Guidelines
+
+## 项目概述
+
+**艾灸椅智能穴位定位系统** — 基于用户体型数据,实现精准穴位定位,控制艾灸椅对准穴位进行理疗。
+
+### 核心文档
+
+| 文档 | 路径 | 说明 |
+|------|------|------|
+| 总需求文档 | `需求工作区/需求材料/艾灸椅功能定义-最新.xlsx` | 艾灸椅硬件+APP完整功能定义 |
+| 接口协议 | `需求工作区/需求材料/艾炙椅接口篇.docx` | APP与设备数据接口协议 |
+| 当前功能 | `需求工作区/需求材料/艾灸取穴智能穴位识别功能列表.xlsx` | 智能穴位识别功能清单 |
+| 设计文档 | `需求工作区/需求材料/design-docs/` | 功能提案、算法设计、用户流程 |
+
+### 项目结构
+
+```
+D:\AI工作台\ajy\
+├── AGENTS.md                    # 本文件 - Agent协作指南
+├── 需求工作区/                   # 需求分析工作区
+│   ├── 需求材料/                # 原始需求文档
+│   │   ├── 艾灸椅功能定义-最新.xlsx
+│   │   ├── 艾炙椅接口篇.docx
+│   │   ├── 艾灸取穴智能穴位识别功能列表.xlsx
+│   │   └── design-docs/        # 设计文档
+│   │       ├── 穴位定位功能提案.md
+│   │       ├── 穴位定位任务清单.md
+│   │       ├── 穴位定位算法设计.md
+│   │       └── 穴位定位用户流程.md
+│   ├── 模版文件/                # 文档模板
+│   └── 需求设计文档/            # 输出设计文档
+└── openspec/                    # 技术实现规范
+    └── aijiyiBackend/          # 后端项目
+```
+
+---
+
+## AI 角色分工
+
+本项目采用**多角色协作**模式,当前AI承担以下角色:
+
+### 角色定义
+
+| 角色 | 职责 | 触发场景 |
+|------|------|----------|
+| **产品经理** | 产品方向定义、优先级排序、业务价值分析 | 产品规划、需求评审 |
+| **需求分析师** | 需求挖掘、用户故事、验收标准定义 | 需求分析、需求澄清 |
+| **系统分析员** | 架构设计、技术方案、接口规范 | 技术选型、系统设计 |
+| **测试工程师** | 测试策略、用例设计、验收测试 | 测试计划、质量保障 |
+
+> **注意**:AI不负责编码实现(开发人员),如需编码请另外安排。
+
+### 协作流程
+
+```
+┌─────────────────────────────────────────────────────────────────────┐
+│                        需求开发流程                                  │
+├─────────────────────────────────────────────────────────────────────┤
+│                                                                     │
+│  需求阶段          设计阶段          验证阶段          交付阶段      │
+│      │                │                │                │          │
+│      ▼                ▼                ▼                ▼          │
+│  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐        │
+│  │产品经理  │───▶│系统分析员│───▶│测试工程师│───▶│产品经理  │        │
+│  │需求分析师│    │         │    │         │    │         │        │
+│  └─────────┘    └─────────┘    └─────────┘    └─────────┘        │
+│      │                │                │                │          │
+│      │ 1.需求分析     │ 2.技术方案     │ 3.测试用例     │ 4.验收    │
+│      │ 2.用户故事     │ 3.接口设计     │ 4.测试执行     │ 5.交付    │
+│      │ 3.验收标准     │ 4.数据模型     │                │          │
+│      │                │                │                │          │
+│      └────────────────┴────────────────┴────────────────┘          │
+│                                                                     │
+└─────────────────────────────────────────────────────────────────────┘
+```
+
+### 角色职责详解
+
+#### 产品经理
+
+**核心职责**:
+- 定义产品愿景和目标
+- 优先级排序和路线图规划
+- 业务价值分析和ROI评估
+- 用户体验和交互设计指导
+- 市场竞品分析
+
+**工作产出**:
+```
+需求工作区/
+├── 产品愿景.md           # 产品方向和目标
+├── 功能优先级.md         # 功能排序和版本规划
+└── 用户画像.md           # 目标用户分析
+```
+
+#### 需求分析师
+
+**核心职责**:
+- 分析原始需求文档,提取关键信息
+- 将业务需求转化为用户故事
+- 定义验收标准和边界条件
+- 澄清需求歧义,确认假设
+- 输出结构化的需求文档
+
+**工作产出**:
+```
+需求工作区/需求设计文档/
+├── {feature}-proposal.md         # 功能提案
+├── {feature}-user-stories.md     # 用户故事
+├── {feature}-acceptance.md       # 验收标准
+└── {feature}-edge-cases.md       # 边界条件
+```
+
+#### 系统分析员
+
+**核心职责**:
+- 技术选型和架构设计
+- 定义API接口规范
+- 设计数据模型和数据库schema
+- 识别技术风险和依赖
+- 输出技术设计方案
+
+**工作产出**:
+```
+需求工作区/需求设计文档/
+├── {feature}-design.md           # 技术设计方案
+├── {feature}-api-spec.yaml       # API接口定义
+├── {feature}-data-model.md       # 数据模型设计
+└── {feature}-tasks.md            # 开发任务拆解
+```
+
+#### 测试工程师
+
+**核心职责**:
+- 根据验收标准设计测试用例
+- 定义测试场景(正常流、异常流、边界条件)
+- 编写测试脚本
+- 执行测试并报告结果
+- 质量保障和风险评估
+
+**工作产出**:
+```
+需求工作区/需求设计文档/
+├── {feature}-test-strategy.md    # 测试策略
+├── {feature}-test-cases.md       # 测试用例
+└── {feature}-test-report.md      # 测试报告
+```
+
+### 角色切换规则
+
+在对话中通过角色声明切换:
+
+```
+[产品经理] 定义产品愿景和优先级...
+[需求分析师] 分析用户登录功能需求...
+[系统分析员] 设计用户认证架构方案...
+[测试工程师] 设计登录功能测试用例...
+```
+
+**默认角色**:根据任务类型自动识别
+- 产品规划 → 产品经理
+- 需求澄清 → 需求分析师
+- 架构讨论 → 系统分析员
+- 测试验收 → 测试工程师
+
+### 质量门禁
+
+每个阶段必须满足以下条件才能进入下一阶段:
+
+| 阶段 | 进入条件 | 输出物 |
+|------|----------|--------|
+| 需求 → 设计 | 需求文档已确认,验收标准已定义 | proposal.md, acceptance.md |
+| 设计 → 开发 | 技术方案已评审,任务已拆解 | design.md, tasks.md |
+| 开发 → 验收 | 代码实现完成,单元测试通过 | 源代码, 单元测试 |
+| 验收 → 完成 | 验收测试通过,无阻塞性缺陷 | 测试报告 |
+
+---
+
+## 当前功能:智能穴位定位
+
+### 功能概述
+
+基于用户体型数据(肩宽、身长、指寸),计算12个常用穴位的相对位置,生成穴位对照表,支持艾灸椅精准定位穴位进行理疗。
+
+### 核心算法
+
+1. **指寸估算**:当用户未提供指寸数据时,根据肩宽和身长估算
+   - 男性:肩宽权重 0.6,身长权重 0.4
+   - 女性:肩宽权重 0.4,身长权重 0.6
+
+2. **穴位计算**:以大椎穴为基准点,计算各穴位相对偏移
+   - 督脉穴位(正中):仅纵向偏移
+   - 膀胱经穴位(双侧):纵向+横向偏移
+
+3. **置信度评分**:根据数据完整度评估定位精度
+   - 仅肩宽+身长:50%置信度,±5cm精度
+   - 完整指寸数据:95%置信度,±2cm精度
+
+### 支持的穴位(12个)
+
+| 穴位 | 经络 | 相对大椎穴位置 | 功效 |
+|------|------|----------------|------|
+| 大椎穴 | 督脉 | 基准点 (0, 0) | 驱寒、祛湿、解表 |
+| 身柱穴 | 督脉 | 下3寸 | 止咳平喘、安神 |
+| 至阳穴 | 督脉 | 下7寸 | 宽胸理气 |
+| 命门穴 | 督脉 | 下14寸 | 补肾壮阳 |
+| 腰阳关穴 | 督脉 | 下17寸 | 强腰健肾 |
+| 风门穴(双) | 膀胱经 | 下1.5寸,旁开1.5寸 | 祛风解表 |
+| 秉风穴(双) | 小肠经 | 下3寸,旁开3寸 | 祛风散寒 |
+| 肺俞穴(双) | 膀胱经 | 下3寸,旁开1.5寸 | 化痰止咳 |
+| 膈俞穴(双) | 膀胱经 | 下7寸,旁开1.5寸 | 活血化瘀 |
+| 脾俞穴(双) | 膀胱经 | 下11寸,旁开1.5寸 | 健脾养血 |
+| 肾俞穴(双) | 膀胱经 | 下14寸,旁开1.5寸 | 补肾益气 |
+| 八髎穴 | 膀胱经 | 下16寸 | 调理下焦 |
+
+### 数据验证规则
+
+| 字段 | 男性范围 | 女性范围 | 单位 |
+|------|---------|---------|------|
+| 肩宽 | 36-52 | 32-46 | cm |
+| 身长(坐高) | 75-95 | 70-88 | cm |
+| 一寸 | 1.5-2.8 | 1.3-2.4 | cm |
+| 1.5寸 | 2.2-4.0 | 2.0-3.5 | cm |
+| 三寸 | 4.5-8.0 | 4.0-7.0 | cm |
+
+### 验收标准
+
+1. 穴位定位精度 ≤ 2cm(指寸数据完整时)
+2. 经络图支持点击、缩放交互
+3. 症状推荐覆盖率 80%+
+4. API响应时间 < 200ms
+
+---
+
+## 通信协议
+
+### 蓝牙BLE 5.0
+
+| 特征值UUID | 用途 | 方向 |
+|------------|------|------|
+| `0000FFF1-0000-1000-8000-00805F9B34FB` | 命令写入 | APP → 设备 |
+| `0000FFF2-0000-1000-8000-00805F9B34FB` | 数据通知 | 设备 → APP |
+| `0000FFF3-0000-1000-8000-00805F9B34FB` | 用户档案写入 | APP → 设备 |
+| `0000FFF4-0000-1000-8000-00805F9B34FB` | 穴位对照表通知 | 设备 → APP |
+
+### 命令类型
+
+| 命令码 | 名称 | 说明 |
+|--------|------|------|
+| `0x01` | AUTH_REQUEST | 连接认证请求 |
+| `0x02` | AUTH_RESPONSE | 认证响应 |
+| `0x10` | GET_USER_LIST | 获取用户列表 |
+| `0x11` | CREATE_USER | 创建用户 |
+| `0x12` | SELECT_USER | 选择用户 |
+| `0x13` | DELETE_USER | 删除用户 |
+| `0x20` | START_DETECTION | 开始检测 |
+| `0x21` | DAZHUI_POSITION | 大椎穴位置上报 |
+| `0x22` | ACUPOINT_TABLE_SYNC | 穴位对照表同步 |
+| `0x30` | START_TREATMENT | 开始治疗 |
+| `0x31` | PAUSE_TREATMENT | 暂停治疗 |
+| `0x32` | RESUME_TREATMENT | 恢复治疗 |
+| `0x33` | END_TREATMENT | 结束治疗 |
+| `0x40` | DEVICE_STATUS | 设备状态查询 |
+| `0x41` | ERROR_REPORT | 错误上报 |
+
+---
+
+## API接口设计
+
+| 方法 | 路径 | 描述 |
+|------|------|------|
+| POST | `/api/users/:id/acupoint-table` | 生成用户穴位对照表 |
+| POST | `/api/users/:id/acupoint-table/validate` | 验证输入数据 |
+| GET | `/api/users/:id/acupoint-table` | 获取用户穴位对照表 |
+| POST | `/api/treatment-sessions` | 创建治疗会话 |
+| POST | `/api/treatment-sessions/:id/start` | 开始治疗 |
+| GET | `/api/acupoints` | 获取所有穴位基础数据 |
+| GET | `/api/acupoints/:id` | 获取单个穴位详情 |
+
+---
+
+## Git 规范
+
+- 使用功能分支开发
+- 提交信息格式:`type(scope): description`
+  - `feat`: 新功能
+  - `fix`: 修复bug
+  - `docs`: 文档更新
+  - `refactor`: 代码重构
+  - `test`: 测试相关
+- 推送前运行代码检查(配置后)
+
+## 安全规范
+
+- 不提交密钥、API密钥或凭证
+- 使用环境变量存储敏感配置
+- 验证和清理所有用户输入
+
+---
+
+## 参考标准
+
+- **GB/T 12346-2006**《腧穴名称与定位》
+- **GB/T 10000-1988**《中国成年人人体尺寸》
+- 《针灸学》教材(穴位定位方法)

+ 0 - 0
code/README.md → README.md


+ 0 - 23
code/.gitignore

@@ -1,23 +0,0 @@
-# Java / Maven
-target/
-*.class
-*.jar
-*.war
-*.ear
-
-# IDE
-.idea/
-*.iml
-.classpath
-.project
-.settings/
-.vscode/
-
-# Node.js
-node_modules/
-dist/
-
-# OS
-.DS_Store
-Thumbs.db
-openspec/.qoder

+ 0 - 306
code/AGENTS.md

@@ -1,306 +0,0 @@
-# Agent Guidelines
-
-## 项目概述
-
-**艾灸椅智能穴位定位系统** — 基于用户体型数据,实现精准穴位定位,控制艾灸椅对准穴位进行理疗。
-
-### 核心文档
-
-| 文档 | 路径 | 说明 |
-|------|------|------|
-| 总需求文档 | `需求工作区/需求材料/艾灸椅功能定义-最新.xlsx` | 艾灸椅硬件+APP完整功能定义 |
-| 接口协议 | `需求工作区/需求材料/艾炙椅接口篇.docx` | APP与设备数据接口协议 |
-| 当前功能 | `需求工作区/需求材料/艾灸取穴智能穴位识别功能列表.xlsx` | 智能穴位识别功能清单 |
-| 设计文档 | `需求工作区/需求材料/design-docs/` | 功能提案、算法设计、用户流程 |
-
-### 项目结构
-
-```
-D:\AI工作台\ajy\
-├── AGENTS.md                    # 本文件 - Agent协作指南
-├── 需求工作区/                   # 需求分析工作区
-│   ├── 需求材料/                # 原始需求文档
-│   │   ├── 艾灸椅功能定义-最新.xlsx
-│   │   ├── 艾炙椅接口篇.docx
-│   │   ├── 艾灸取穴智能穴位识别功能列表.xlsx
-│   │   └── design-docs/        # 设计文档
-│   │       ├── 穴位定位功能提案.md
-│   │       ├── 穴位定位任务清单.md
-│   │       ├── 穴位定位算法设计.md
-│   │       └── 穴位定位用户流程.md
-│   ├── 模版文件/                # 文档模板
-│   └── 需求设计文档/            # 输出设计文档
-└── openspec/                    # 技术实现规范
-    └── aijiyiBackend/          # 后端项目
-```
-
----
-
-## AI 角色分工
-
-本项目采用**多角色协作**模式,当前AI承担以下角色:
-
-### 角色定义
-
-| 角色 | 职责 | 触发场景 |
-|------|------|----------|
-| **产品经理** | 产品方向定义、优先级排序、业务价值分析 | 产品规划、需求评审 |
-| **需求分析师** | 需求挖掘、用户故事、验收标准定义 | 需求分析、需求澄清 |
-| **系统分析员** | 架构设计、技术方案、接口规范 | 技术选型、系统设计 |
-| **测试工程师** | 测试策略、用例设计、验收测试 | 测试计划、质量保障 |
-
-> **注意**:AI不负责编码实现(开发人员),如需编码请另外安排。
-
-### 协作流程
-
-```
-┌─────────────────────────────────────────────────────────────────────┐
-│                        需求开发流程                                  │
-├─────────────────────────────────────────────────────────────────────┤
-│                                                                     │
-│  需求阶段          设计阶段          验证阶段          交付阶段      │
-│      │                │                │                │          │
-│      ▼                ▼                ▼                ▼          │
-│  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐        │
-│  │产品经理  │───▶│系统分析员│───▶│测试工程师│───▶│产品经理  │        │
-│  │需求分析师│    │         │    │         │    │         │        │
-│  └─────────┘    └─────────┘    └─────────┘    └─────────┘        │
-│      │                │                │                │          │
-│      │ 1.需求分析     │ 2.技术方案     │ 3.测试用例     │ 4.验收    │
-│      │ 2.用户故事     │ 3.接口设计     │ 4.测试执行     │ 5.交付    │
-│      │ 3.验收标准     │ 4.数据模型     │                │          │
-│      │                │                │                │          │
-│      └────────────────┴────────────────┴────────────────┘          │
-│                                                                     │
-└─────────────────────────────────────────────────────────────────────┘
-```
-
-### 角色职责详解
-
-#### 产品经理
-
-**核心职责**:
-- 定义产品愿景和目标
-- 优先级排序和路线图规划
-- 业务价值分析和ROI评估
-- 用户体验和交互设计指导
-- 市场竞品分析
-
-**工作产出**:
-```
-需求工作区/
-├── 产品愿景.md           # 产品方向和目标
-├── 功能优先级.md         # 功能排序和版本规划
-└── 用户画像.md           # 目标用户分析
-```
-
-#### 需求分析师
-
-**核心职责**:
-- 分析原始需求文档,提取关键信息
-- 将业务需求转化为用户故事
-- 定义验收标准和边界条件
-- 澄清需求歧义,确认假设
-- 输出结构化的需求文档
-
-**工作产出**:
-```
-需求工作区/需求设计文档/
-├── {feature}-proposal.md         # 功能提案
-├── {feature}-user-stories.md     # 用户故事
-├── {feature}-acceptance.md       # 验收标准
-└── {feature}-edge-cases.md       # 边界条件
-```
-
-#### 系统分析员
-
-**核心职责**:
-- 技术选型和架构设计
-- 定义API接口规范
-- 设计数据模型和数据库schema
-- 识别技术风险和依赖
-- 输出技术设计方案
-
-**工作产出**:
-```
-需求工作区/需求设计文档/
-├── {feature}-design.md           # 技术设计方案
-├── {feature}-api-spec.yaml       # API接口定义
-├── {feature}-data-model.md       # 数据模型设计
-└── {feature}-tasks.md            # 开发任务拆解
-```
-
-#### 测试工程师
-
-**核心职责**:
-- 根据验收标准设计测试用例
-- 定义测试场景(正常流、异常流、边界条件)
-- 编写测试脚本
-- 执行测试并报告结果
-- 质量保障和风险评估
-
-**工作产出**:
-```
-需求工作区/需求设计文档/
-├── {feature}-test-strategy.md    # 测试策略
-├── {feature}-test-cases.md       # 测试用例
-└── {feature}-test-report.md      # 测试报告
-```
-
-### 角色切换规则
-
-在对话中通过角色声明切换:
-
-```
-[产品经理] 定义产品愿景和优先级...
-[需求分析师] 分析用户登录功能需求...
-[系统分析员] 设计用户认证架构方案...
-[测试工程师] 设计登录功能测试用例...
-```
-
-**默认角色**:根据任务类型自动识别
-- 产品规划 → 产品经理
-- 需求澄清 → 需求分析师
-- 架构讨论 → 系统分析员
-- 测试验收 → 测试工程师
-
-### 质量门禁
-
-每个阶段必须满足以下条件才能进入下一阶段:
-
-| 阶段 | 进入条件 | 输出物 |
-|------|----------|--------|
-| 需求 → 设计 | 需求文档已确认,验收标准已定义 | proposal.md, acceptance.md |
-| 设计 → 开发 | 技术方案已评审,任务已拆解 | design.md, tasks.md |
-| 开发 → 验收 | 代码实现完成,单元测试通过 | 源代码, 单元测试 |
-| 验收 → 完成 | 验收测试通过,无阻塞性缺陷 | 测试报告 |
-
----
-
-## 当前功能:智能穴位定位
-
-### 功能概述
-
-基于用户体型数据(肩宽、身长、指寸),计算12个常用穴位的相对位置,生成穴位对照表,支持艾灸椅精准定位穴位进行理疗。
-
-### 核心算法
-
-1. **指寸估算**:当用户未提供指寸数据时,根据肩宽和身长估算
-   - 男性:肩宽权重 0.6,身长权重 0.4
-   - 女性:肩宽权重 0.4,身长权重 0.6
-
-2. **穴位计算**:以大椎穴为基准点,计算各穴位相对偏移
-   - 督脉穴位(正中):仅纵向偏移
-   - 膀胱经穴位(双侧):纵向+横向偏移
-
-3. **置信度评分**:根据数据完整度评估定位精度
-   - 仅肩宽+身长:50%置信度,±5cm精度
-   - 完整指寸数据:95%置信度,±2cm精度
-
-### 支持的穴位(12个)
-
-| 穴位 | 经络 | 相对大椎穴位置 | 功效 |
-|------|------|----------------|------|
-| 大椎穴 | 督脉 | 基准点 (0, 0) | 驱寒、祛湿、解表 |
-| 身柱穴 | 督脉 | 下3寸 | 止咳平喘、安神 |
-| 至阳穴 | 督脉 | 下7寸 | 宽胸理气 |
-| 命门穴 | 督脉 | 下14寸 | 补肾壮阳 |
-| 腰阳关穴 | 督脉 | 下17寸 | 强腰健肾 |
-| 风门穴(双) | 膀胱经 | 下1.5寸,旁开1.5寸 | 祛风解表 |
-| 秉风穴(双) | 小肠经 | 下3寸,旁开3寸 | 祛风散寒 |
-| 肺俞穴(双) | 膀胱经 | 下3寸,旁开1.5寸 | 化痰止咳 |
-| 膈俞穴(双) | 膀胱经 | 下7寸,旁开1.5寸 | 活血化瘀 |
-| 脾俞穴(双) | 膀胱经 | 下11寸,旁开1.5寸 | 健脾养血 |
-| 肾俞穴(双) | 膀胱经 | 下14寸,旁开1.5寸 | 补肾益气 |
-| 八髎穴 | 膀胱经 | 下16寸 | 调理下焦 |
-
-### 数据验证规则
-
-| 字段 | 男性范围 | 女性范围 | 单位 |
-|------|---------|---------|------|
-| 肩宽 | 36-52 | 32-46 | cm |
-| 身长(坐高) | 75-95 | 70-88 | cm |
-| 一寸 | 1.5-2.8 | 1.3-2.4 | cm |
-| 1.5寸 | 2.2-4.0 | 2.0-3.5 | cm |
-| 三寸 | 4.5-8.0 | 4.0-7.0 | cm |
-
-### 验收标准
-
-1. 穴位定位精度 ≤ 2cm(指寸数据完整时)
-2. 经络图支持点击、缩放交互
-3. 症状推荐覆盖率 80%+
-4. API响应时间 < 200ms
-
----
-
-## 通信协议
-
-### 蓝牙BLE 5.0
-
-| 特征值UUID | 用途 | 方向 |
-|------------|------|------|
-| `0000FFF1-0000-1000-8000-00805F9B34FB` | 命令写入 | APP → 设备 |
-| `0000FFF2-0000-1000-8000-00805F9B34FB` | 数据通知 | 设备 → APP |
-| `0000FFF3-0000-1000-8000-00805F9B34FB` | 用户档案写入 | APP → 设备 |
-| `0000FFF4-0000-1000-8000-00805F9B34FB` | 穴位对照表通知 | 设备 → APP |
-
-### 命令类型
-
-| 命令码 | 名称 | 说明 |
-|--------|------|------|
-| `0x01` | AUTH_REQUEST | 连接认证请求 |
-| `0x02` | AUTH_RESPONSE | 认证响应 |
-| `0x10` | GET_USER_LIST | 获取用户列表 |
-| `0x11` | CREATE_USER | 创建用户 |
-| `0x12` | SELECT_USER | 选择用户 |
-| `0x13` | DELETE_USER | 删除用户 |
-| `0x20` | START_DETECTION | 开始检测 |
-| `0x21` | DAZHUI_POSITION | 大椎穴位置上报 |
-| `0x22` | ACUPOINT_TABLE_SYNC | 穴位对照表同步 |
-| `0x30` | START_TREATMENT | 开始治疗 |
-| `0x31` | PAUSE_TREATMENT | 暂停治疗 |
-| `0x32` | RESUME_TREATMENT | 恢复治疗 |
-| `0x33` | END_TREATMENT | 结束治疗 |
-| `0x40` | DEVICE_STATUS | 设备状态查询 |
-| `0x41` | ERROR_REPORT | 错误上报 |
-
----
-
-## API接口设计
-
-| 方法 | 路径 | 描述 |
-|------|------|------|
-| POST | `/api/users/:id/acupoint-table` | 生成用户穴位对照表 |
-| POST | `/api/users/:id/acupoint-table/validate` | 验证输入数据 |
-| GET | `/api/users/:id/acupoint-table` | 获取用户穴位对照表 |
-| POST | `/api/treatment-sessions` | 创建治疗会话 |
-| POST | `/api/treatment-sessions/:id/start` | 开始治疗 |
-| GET | `/api/acupoints` | 获取所有穴位基础数据 |
-| GET | `/api/acupoints/:id` | 获取单个穴位详情 |
-
----
-
-## Git 规范
-
-- 使用功能分支开发
-- 提交信息格式:`type(scope): description`
-  - `feat`: 新功能
-  - `fix`: 修复bug
-  - `docs`: 文档更新
-  - `refactor`: 代码重构
-  - `test`: 测试相关
-- 推送前运行代码检查(配置后)
-
-## 安全规范
-
-- 不提交密钥、API密钥或凭证
-- 使用环境变量存储敏感配置
-- 验证和清理所有用户输入
-
----
-
-## 参考标准
-
-- **GB/T 12346-2006**《腧穴名称与定位》
-- **GB/T 10000-1988**《中国成年人人体尺寸》
-- 《针灸学》教材(穴位定位方法)

+ 0 - 31
code/backend/src/main/java/com/aijiuyi/admin/common/annotation/Log.java

@@ -1,31 +0,0 @@
-package com.aijiuyi.admin.common.annotation;
-
-import com.aijiuyi.admin.common.enums.OperationType;
-
-import java.lang.annotation.*;
-
-/**
- * 操作日志注解
- * 标注在 Controller 方法上,记录接口说明、模块和操作类型到操作日志表
- * 示例:@Log(value = "查询用户列表", module = "用户管理", operationType = OperationType.QUERY)
- */
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface Log {
-
-    /**
-     * 接口说明(将记录到日志表的 api_desc 字段)
-     */
-    String value() default "";
-
-    /**
-     * 操作模块(将记录到日志表的 operation_module 字段)
-     */
-    String module() default "";
-
-    /**
-     * 操作类型,默认 OTHER
-     */
-    OperationType operationType() default OperationType.OTHER;
-}

+ 0 - 14
code/backend/src/main/java/com/aijiuyi/admin/common/annotation/NoAuth.java

@@ -1,14 +0,0 @@
-package com.aijiuyi.admin.common.annotation;
-
-import java.lang.annotation.*;
-
-/**
- * 免登录注解(不需要 Token 鉴权)
- * 可标注在 Controller 类或方法上,标注后该接口不需要携带 Token 即可访问
- * 示例:@NoAuth 标注在登录接口方法上
- */
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface NoAuth {
-}

+ 0 - 176
code/backend/src/main/java/com/aijiuyi/admin/common/aspect/LogAspect.java

@@ -1,176 +0,0 @@
-package com.aijiuyi.admin.common.aspect;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.context.RequestContext;
-import com.aijiuyi.admin.common.entity.SysLog;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.common.service.SysLogService;
-import com.aijiuyi.admin.common.util.IpUtil;
-import com.aijiuyi.admin.common.util.LogUtil;
-import com.alibaba.fastjson.JSON;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.Around;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.lang.reflect.Method;
-import java.time.LocalDateTime;
-
-/**
- * 操作日志 AOP 切面
- * 拦截所有 @RestController 和 @Controller 类中的方法:
- * 1. 自动记录请求URL、IP、参数、返回值、耗时到 sys_log 表
- * 2. 如果方法上有 @Log 注解,还会记录接口说明、模块、操作类型
- * 3. 没有 @Log 注解时,操作类型根据 HTTP Method 自动推断
- * 日志保存为异步操作,不影响接口响应速度
- */
-@Aspect
-@Component
-public class LogAspect {
-
-    @Autowired
-    private SysLogService sysLogService;
-
-    /**
-     * 切点:所有 @RestController 和 @Controller 类中的方法
-     */
-    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *) " +
-              "|| within(@org.springframework.stereotype.Controller *)")
-    public void controllerPointcut() {
-    }
-
-    /**
-     * 环绕通知:拦截所有 Controller 方法,记录操作日志
-     *
-     * @param point 连接点
-     * @return 方法执行结果
-     * @throws Throwable 异常
-     */
-    @Around("controllerPointcut()")
-    public Object around(ProceedingJoinPoint point) throws Throwable {
-        long startTime = System.currentTimeMillis();
-
-        // 获取当前 HTTP 请求
-        HttpServletRequest request = null;
-        try {
-            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-            if (attributes != null) {
-                request = attributes.getRequest();
-            }
-        } catch (Exception e) {
-            LogUtil.warn(LogAspect.class, "获取 HttpServletRequest 失败: {}", e.getMessage());
-        }
-
-        // 获取方法签名和 @Log 注解
-        MethodSignature signature = (MethodSignature) point.getSignature();
-        Method method = signature.getMethod();
-        Log logAnnotation = method.getAnnotation(Log.class);
-
-        // 构建日志对象
-        SysLog sysLog = new SysLog();
-        sysLog.setRequestId(RequestContext.getRequestId());
-        sysLog.setUserId(RequestContext.getUserId());
-        sysLog.setUserName(RequestContext.getUserName());
-        sysLog.setUserType(RequestContext.getUserType());
-        sysLog.setCreateTime(LocalDateTime.now());
-
-        // 设置请求URL和IP
-        if (request != null) {
-            sysLog.setRequestUrl(request.getRequestURI());
-            sysLog.setRequestIp(IpUtil.getIpAddr(request));
-        }
-
-        // 序列化请求参数(过滤掉文件、请求、响应等不可序列化的参数)
-        try {
-            Object[] args = point.getArgs();
-            if (args != null && args.length > 0) {
-                Object[] serializableArgs = java.util.Arrays.stream(args)
-                        .filter(arg -> !(arg instanceof MultipartFile)
-                                && !(arg instanceof HttpServletRequest)
-                                && !(arg instanceof HttpServletResponse))
-                        .toArray();
-                if (serializableArgs.length > 0) {
-                    sysLog.setRequestParams(JSON.toJSONString(serializableArgs));
-                }
-            }
-        } catch (Exception e) {
-            LogUtil.warn(LogAspect.class, "序列化请求参数失败: {}", e.getMessage());
-        }
-
-        // 填充 @Log 注解中的信息(如无注解,根据 HTTP Method 推断操作类型)
-        if (logAnnotation != null) {
-            // 使用注解中定义的信息
-            sysLog.setApiDesc(logAnnotation.value());
-            sysLog.setOperationModule(logAnnotation.module());
-            sysLog.setOperationType(logAnnotation.operationType().getDesc());
-        } else {
-            // 根据 HTTP Method 自动推断操作类型
-            if (request != null) {
-                sysLog.setOperationType(inferOperationType(request.getMethod()));
-            }
-        }
-
-        // 执行目标方法
-        Object result = null;
-        try {
-            result = point.proceed();
-            // 记录返回结果(截断过长的响应)
-            try {
-                String resultStr = JSON.toJSONString(result);
-                // 超过2000字符则截断
-                if (resultStr.length() > 2000) {
-                    resultStr = resultStr.substring(0, 2000) + "...(已截断)";
-                }
-                sysLog.setRequestResult(resultStr);
-            } catch (Exception e) {
-                LogUtil.warn(LogAspect.class, "序列化返回结果失败: {}", e.getMessage());
-            }
-        } catch (Throwable throwable) {
-            // 记录异常信息
-            sysLog.setRequestResult("异常: " + throwable.getMessage());
-            throw throwable;
-        } finally {
-            // 记录接口耗时日志
-            long costTime = System.currentTimeMillis() - startTime;
-            LogUtil.info(LogAspect.class, "接口[{}] 耗时[{}ms]", sysLog.getRequestUrl(), costTime);
-            // 异步保存日志(不阻塞主流程)
-            try {
-                sysLogService.saveLog(sysLog);
-            } catch (Exception e) {
-                LogUtil.error(LogAspect.class, "保存操作日志失败: {}", e.getMessage());
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * 根据 HTTP Method 推断操作类型
-     *
-     * @param httpMethod HTTP 请求方法(GET/POST/PUT/DELETE等)
-     * @return 操作类型描述
-     */
-    private String inferOperationType(String httpMethod) {
-        switch (httpMethod.toUpperCase()) {
-            case "GET":
-                return OperationType.QUERY.getDesc();
-            case "POST":
-                return OperationType.INSERT.getDesc();
-            case "PUT":
-            case "PATCH":
-                return OperationType.UPDATE.getDesc();
-            case "DELETE":
-                return OperationType.DELETE.getDesc();
-            default:
-                return OperationType.OTHER.getDesc();
-        }
-    }
-}

+ 0 - 105
code/backend/src/main/java/com/aijiuyi/admin/common/config/AcupointSchemaInitializer.java

@@ -1,105 +0,0 @@
-package com.aijiuyi.admin.common.config;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Component;
-
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-/**
- * Keeps legacy acupoint tables compatible with the current relative-coordinate parser.
- */
-@Component
-@Order(0)
-public class AcupointSchemaInitializer implements CommandLineRunner {
-
-    private static final Logger log = LoggerFactory.getLogger(AcupointSchemaInitializer.class);
-    private static final String TABLE_NAME = "acupoint";
-
-    private final DataSource dataSource;
-
-    public AcupointSchemaInitializer(DataSource dataSource) {
-        this.dataSource = dataSource;
-    }
-
-    @Override
-    public void run(String... args) {
-        try (Connection connection = dataSource.getConnection()) {
-            if (!tableExists(connection, TABLE_NAME)) {
-                log.info("Skip acupoint schema initialization because table [{}] does not exist", TABLE_NAME);
-                return;
-            }
-
-            addColumnIfMissing(connection, "parse_status",
-                    "varchar(32) DEFAULT NULL COMMENT 'Acupoint parser status'");
-            addColumnIfMissing(connection, "parse_confidence",
-                    "varchar(16) DEFAULT NULL COMMENT 'Acupoint parser confidence'");
-            addColumnIfMissing(connection, "parse_message",
-                    "varchar(500) DEFAULT NULL COMMENT 'Acupoint parser message'");
-
-            executeUpdate(connection, "UPDATE acupoint SET location_type = 1 WHERE deleted = 0 AND (location_type IS NULL OR location_type <> 1)");
-            executeUpdate(connection, "UPDATE acupoint SET parse_status = 'parsed' WHERE deleted = 0 AND parse_status IS NULL AND y_reference_term IS NOT NULL AND normalized_y IS NOT NULL AND (side = 1 OR lateral_offset IS NOT NULL OR acupoint_code IN ('pangguang-31','pangguang-32','pangguang-33','pangguang-34') OR name IN ('上髎','次髎','中髎','下髎'))");
-            executeUpdate(connection, "UPDATE acupoint SET parse_status = 'review_required' WHERE deleted = 0 AND parse_status IS NULL");
-            executeUpdate(connection, "UPDATE acupoint SET parse_confidence = CASE WHEN parse_status = 'parsed' THEN 'medium' ELSE 'low' END WHERE deleted = 0 AND parse_confidence IS NULL");
-
-            log.info("Acupoint schema initialization completed");
-        } catch (SQLException e) {
-            throw new IllegalStateException("Failed to initialize acupoint schema", e);
-        }
-    }
-
-    private boolean tableExists(Connection connection, String tableName) throws SQLException {
-        DatabaseMetaData metaData = connection.getMetaData();
-        String catalog = connection.getCatalog();
-        try (ResultSet resultSet = metaData.getTables(catalog, null, tableName, new String[]{"TABLE"})) {
-            if (resultSet.next()) {
-                return true;
-            }
-        }
-        try (ResultSet resultSet = metaData.getTables(catalog, null, tableName.toUpperCase(), new String[]{"TABLE"})) {
-            return resultSet.next();
-        }
-    }
-
-    private boolean columnExists(Connection connection, String columnName) throws SQLException {
-        DatabaseMetaData metaData = connection.getMetaData();
-        String catalog = connection.getCatalog();
-        try (ResultSet resultSet = metaData.getColumns(catalog, null, TABLE_NAME, columnName)) {
-            if (resultSet.next()) {
-                return true;
-            }
-        }
-        try (ResultSet resultSet = metaData.getColumns(catalog, null, TABLE_NAME, columnName.toUpperCase())) {
-            return resultSet.next();
-        }
-    }
-
-    private void addColumnIfMissing(Connection connection, String columnName, String definition) throws SQLException {
-        if (columnExists(connection, columnName)) {
-            return;
-        }
-        try {
-            executeUpdate(connection, "ALTER TABLE acupoint ADD COLUMN " + columnName + " " + definition);
-        } catch (SQLException e) {
-            if (e.getErrorCode() == 1060) {
-                log.info("Acupoint column [{}] already exists", columnName);
-                return;
-            }
-            throw e;
-        }
-        log.info("Added missing acupoint column [{}]", columnName);
-    }
-
-    private void executeUpdate(Connection connection, String sql) throws SQLException {
-        try (Statement statement = connection.createStatement()) {
-            statement.executeUpdate(sql);
-        }
-    }
-}

+ 0 - 39
code/backend/src/main/java/com/aijiuyi/admin/common/config/AsyncConfig.java

@@ -1,39 +0,0 @@
-package com.aijiuyi.admin.common.config;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.ThreadPoolExecutor;
-
-/**
- * 异步线程池配置
- * 供 @Async("asyncExecutor") 使用,主要用于异步保存操作日志
- */
-@Configuration
-public class AsyncConfig {
-
-    /**
-     * 异步任务线程池
-     * 核心线程数5,最大线程数20,队列容量100
-     *
-     * @return 线程池执行器
-     */
-    @Bean("asyncExecutor")
-    public Executor asyncExecutor() {
-        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
-        // 核心线程数
-        executor.setCorePoolSize(5);
-        // 最大线程数
-        executor.setMaxPoolSize(20);
-        // 任务队列容量
-        executor.setQueueCapacity(100);
-        // 线程名前缀
-        executor.setThreadNamePrefix("async-task-");
-        // 拒绝策略:由调用线程执行,保证日志不丢失
-        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
-        executor.initialize();
-        return executor;
-    }
-}

+ 0 - 29
code/backend/src/main/java/com/aijiuyi/admin/common/config/AuthProperties.java

@@ -1,29 +0,0 @@
-package com.aijiuyi.admin.common.config;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 鉴权配置属性
- * 从 application.yml 中读取 auth 前缀的配置
- * 配置不需要 Token 鉴权的接口白名单
- */
-@Data
-@Component
-@ConfigurationProperties(prefix = "auth")
-public class AuthProperties {
-
-    /**
-     * 不需要 Token 鉴权的接口白名单(支持 Ant 风格路径匹配)
-     * 在 application.yml 中配置:
-     * auth:
-     *   white-list:
-     *     - /api/auth/login
-     *     - /api/auth/register
-     */
-    private List<String> whiteList = new ArrayList<>();
-}

+ 0 - 29
code/backend/src/main/java/com/aijiuyi/admin/common/config/MybatisPlusConfig.java

@@ -1,29 +0,0 @@
-package com.aijiuyi.admin.common.config;
-
-import com.baomidou.mybatisplus.annotation.DbType;
-import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * MyBatis Plus 配置类
- * 注册分页插件等 MyBatis Plus 扩展
- */
-@Configuration
-public class MybatisPlusConfig {
-
-    /**
-     * 注册 MyBatis Plus 拦截器
-     * 当前包含:MySQL 分页插件
-     *
-     * @return MybatisPlusInterceptor
-     */
-    @Bean
-    public MybatisPlusInterceptor mybatisPlusInterceptor() {
-        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
-        // 添加 MySQL 分页插件
-        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
-        return interceptor;
-    }
-}

+ 0 - 207
code/backend/src/main/java/com/aijiuyi/admin/common/config/PlanDataInitializer.java

@@ -1,207 +0,0 @@
-package com.aijiuyi.admin.common.config;
-
-import com.aijiuyi.admin.entity.Plan;
-import com.aijiuyi.admin.entity.PlanStep;
-import com.aijiuyi.admin.mapper.PlanMapper;
-import com.aijiuyi.admin.mapper.PlanStepMapper;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.stereotype.Component;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * 启动时确保系统内置方案存在。
- */
-@Component
-public class PlanDataInitializer implements CommandLineRunner {
-
-    @Autowired
-    private PlanMapper planMapper;
-
-    @Autowired
-    private PlanStepMapper planStepMapper;
-
-    @Override
-    public void run(String... args) {
-        List<PlanSeed> seeds = buildSeeds();
-        for (PlanSeed seed : seeds) {
-            Plan plan = planMapper.selectOne(new LambdaQueryWrapper<Plan>()
-                    .eq(Plan::getPlanCode, seed.planCode)
-                    .last("limit 1"));
-            if (plan == null) {
-                plan = new Plan();
-                plan.setPlanCode(seed.planCode);
-                plan.setApplicableGender(0);
-                plan.setAgeMin(1);
-                plan.setAgeMax(120);
-                plan.setAuthorType(1);
-                plan.setAuthorName("ADMIN");
-                plan.setStatus(1);
-                plan.setUseCount(0);
-                fillPlan(plan, seed);
-                planMapper.insert(plan);
-            } else {
-                fillPlan(plan, seed);
-                planMapper.updateById(plan);
-            }
-
-            planStepMapper.delete(new LambdaUpdateWrapper<PlanStep>()
-                    .eq(PlanStep::getPlanId, plan.getId()));
-
-            for (int i = 0; i < seed.steps.size(); i++) {
-                StepSeed stepSeed = seed.steps.get(i);
-                PlanStep step = new PlanStep();
-                step.setPlanId(plan.getId());
-                step.setStepOrder(i + 1);
-                step.setAcupointId(stepSeed.acupointId);
-                step.setAcupointName(stepSeed.acupointName);
-                step.setSide(stepSeed.side);
-                step.setTemperature(stepSeed.temperature);
-                step.setDuration(stepSeed.duration);
-                step.setTechniqueId(stepSeed.techniqueId);
-                step.setTechniqueName(stepSeed.techniqueName);
-                step.setRemark(stepSeed.remark);
-                planStepMapper.insert(step);
-            }
-        }
-    }
-
-    private void fillPlan(Plan plan, PlanSeed seed) {
-        plan.setName(seed.name);
-        plan.setModeType(seed.modeType);
-        plan.setEffectType(seed.effectType);
-        plan.setSymptoms(seed.symptoms);
-        plan.setDescription(seed.description);
-        plan.setAuthorType(1);
-        plan.setAuthorName("ADMIN");
-        plan.setStatus(1);
-    }
-
-    private List<PlanSeed> buildSeeds() {
-        List<PlanSeed> seeds = new ArrayList<>();
-        seeds.add(new PlanSeed(
-                "S001", "一键艾灸(默认)", 1, null, "日常调理,疲劳,手脚冰凉", "系统默认一键艾灸方案,覆盖基础督脉与膀胱经调理。",
-                Arrays.asList(
-                        new StepSeed(1L, "大椎穴", 1, 42.0, 6, 2L, "温和灸", "默认基准穴位"),
-                        new StepSeed(2L, "风门穴", 4, 43.0, 6, 2L, "温和灸", "双侧温养"),
-                        new StepSeed(3L, "肺俞穴", 4, 43.0, 6, 2L, "温和灸", "提升背部暖感"),
-                        new StepSeed(8L, "肾俞穴", 4, 44.0, 6, 2L, "温和灸", "补益下焦"),
-                        new StepSeed(9L, "命门穴", 1, 45.0, 6, 2L, "温和灸", "固本收尾")
-                )));
-        seeds.add(new PlanSeed(
-                "S002", "专业驱寒方案", 2, "驱寒", "怕冷,手脚冰凉,畏寒", "针对阳虚畏寒人群的基础驱寒方案。",
-                Arrays.asList(
-                        new StepSeed(1L, "大椎穴", 1, 44.0, 8, 3L, "雀啄灸", "驱寒起始"),
-                        new StepSeed(2L, "风门穴", 4, 44.0, 8, 3L, "雀啄灸", "祛寒散表"),
-                        new StepSeed(8L, "肾俞穴", 4, 45.0, 8, 2L, "温和灸", "温补肾阳"),
-                        new StepSeed(9L, "命门穴", 1, 45.0, 8, 2L, "温和灸", "扶阳收尾")
-                )));
-        seeds.add(new PlanSeed(
-                "S003", "专业祛湿方案", 2, "祛湿", "困倦,湿重,食欲差", "围绕脾胃与膀胱经调理的祛湿方案。",
-                Arrays.asList(
-                        new StepSeed(6L, "脾俞穴", 4, 43.0, 8, 2L, "温和灸", "健脾化湿"),
-                        new StepSeed(7L, "胃俞穴", 4, 43.0, 8, 2L, "温和灸", "和胃助运化"),
-                        new StepSeed(10L, "中脘穴", 1, 42.0, 8, 4L, "回旋灸", "中焦祛湿"),
-                        new StepSeed(11L, "气海穴", 1, 42.0, 6, 2L, "温和灸", "益气固本")
-                )));
-        seeds.add(new PlanSeed(
-                "S004", "专业祛风方案", 2, "祛风", "感冒,怕风,鼻塞", "针对风寒风邪引发不适的基础调理方案。",
-                Arrays.asList(
-                        new StepSeed(1L, "大椎穴", 1, 42.0, 7, 2L, "温和灸", "祛风首穴"),
-                        new StepSeed(2L, "风门穴", 4, 43.0, 7, 3L, "雀啄灸", "散风解表"),
-                        new StepSeed(3L, "肺俞穴", 4, 42.0, 7, 2L, "温和灸", "协同宣肺"),
-                        new StepSeed(4L, "心俞穴", 4, 42.0, 6, 2L, "温和灸", "安神扶正")
-                )));
-        seeds.add(new PlanSeed(
-                "S005", "专业化瘀方案", 2, "化瘀", "僵硬,酸痛,局部不适", "用于改善局部瘀滞与经络不畅。",
-                Arrays.asList(
-                        new StepSeed(4L, "心俞穴", 4, 43.0, 8, 4L, "回旋灸", "活络起始"),
-                        new StepSeed(5L, "肝俞穴", 4, 44.0, 8, 4L, "回旋灸", "疏肝化瘀"),
-                        new StepSeed(6L, "脾俞穴", 4, 43.0, 6, 2L, "温和灸", "调和中焦"),
-                        new StepSeed(9L, "命门穴", 1, 44.0, 6, 2L, "温和灸", "温阳通络")
-                )));
-        seeds.add(new PlanSeed(
-                "S006", "专业活血方案", 2, "活血", "疲劳,循环差,肩背紧张", "帮助提升背部循环与整体活力。",
-                Arrays.asList(
-                        new StepSeed(3L, "肺俞穴", 4, 42.0, 7, 4L, "回旋灸", "活血舒展"),
-                        new StepSeed(4L, "心俞穴", 4, 43.0, 7, 4L, "回旋灸", "促进循环"),
-                        new StepSeed(5L, "肝俞穴", 4, 43.0, 7, 4L, "回旋灸", "疏通经络"),
-                        new StepSeed(11L, "气海穴", 1, 42.0, 6, 2L, "温和灸", "补气收束")
-                )));
-        seeds.add(new PlanSeed(
-                "S007", "专业化痰方案", 2, "化痰", "咳嗽,痰多,胸闷", "偏肺俞与中焦调理的化痰方案。",
-                Arrays.asList(
-                        new StepSeed(3L, "肺俞穴", 4, 42.0, 8, 2L, "温和灸", "宣肺止咳"),
-                        new StepSeed(2L, "风门穴", 4, 42.0, 8, 2L, "温和灸", "协同宣散"),
-                        new StepSeed(10L, "中脘穴", 1, 41.0, 7, 4L, "回旋灸", "中焦运化"),
-                        new StepSeed(11L, "气海穴", 1, 41.0, 7, 2L, "温和灸", "益气化痰")
-                )));
-        seeds.add(new PlanSeed(
-                "S008", "专业养颜方案", 2, "养颜", "气色差,疲劳,睡眠一般", "通过脾胃与气海关元综合调理气色。",
-                Arrays.asList(
-                        new StepSeed(6L, "脾俞穴", 4, 42.0, 8, 2L, "温和灸", "健脾养颜"),
-                        new StepSeed(7L, "胃俞穴", 4, 42.0, 8, 2L, "温和灸", "和胃生化"),
-                        new StepSeed(11L, "气海穴", 1, 42.0, 7, 4L, "回旋灸", "补气养神"),
-                        new StepSeed(12L, "关元穴", 1, 42.0, 7, 2L, "温和灸", "养元收尾")
-                )));
-        seeds.add(new PlanSeed(
-                "S009", "专业扶阳方案", 2, "扶阳", "乏力,腰酸,手脚冰凉", "以命门肾俞关元为主的扶阳固本方案。",
-                Arrays.asList(
-                        new StepSeed(8L, "肾俞穴", 4, 44.0, 8, 2L, "温和灸", "补肾扶阳"),
-                        new StepSeed(9L, "命门穴", 1, 45.0, 8, 2L, "温和灸", "培补命门火"),
-                        new StepSeed(11L, "气海穴", 1, 44.0, 7, 2L, "温和灸", "益气助阳"),
-                        new StepSeed(12L, "关元穴", 1, 44.0, 7, 2L, "温和灸", "固本培元")
-                )));
-        return seeds;
-    }
-
-    private static class PlanSeed {
-        private final String planCode;
-        private final String name;
-        private final Integer modeType;
-        private final String effectType;
-        private final String symptoms;
-        private final String description;
-        private final List<StepSeed> steps;
-
-        private PlanSeed(String planCode, String name, Integer modeType, String effectType,
-                         String symptoms, String description, List<StepSeed> steps) {
-            this.planCode = planCode;
-            this.name = name;
-            this.modeType = modeType;
-            this.effectType = effectType;
-            this.symptoms = symptoms;
-            this.description = description;
-            this.steps = steps;
-        }
-    }
-
-    private static class StepSeed {
-        private final Long acupointId;
-        private final String acupointName;
-        private final Integer side;
-        private final BigDecimal temperature;
-        private final Integer duration;
-        private final Long techniqueId;
-        private final String techniqueName;
-        private final String remark;
-
-        private StepSeed(Long acupointId, String acupointName, Integer side, double temperature,
-                         Integer duration, Long techniqueId, String techniqueName, String remark) {
-            this.acupointId = acupointId;
-            this.acupointName = acupointName;
-            this.side = side;
-            this.temperature = BigDecimal.valueOf(temperature);
-            this.duration = duration;
-            this.techniqueId = techniqueId;
-            this.techniqueName = techniqueName;
-            this.remark = remark;
-        }
-    }
-}

+ 0 - 55
code/backend/src/main/java/com/aijiuyi/admin/common/config/RedisConfig.java

@@ -1,55 +0,0 @@
-package com.aijiuyi.admin.common.config;
-
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.redis.connection.RedisConnectionFactory;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
-import org.springframework.data.redis.serializer.StringRedisSerializer;
-
-/**
- * Redis 配置类
- * 自定义序列化方式,key 使用 String 序列化,value 使用 JSON 序列化
- * 解决默认 JDK 序列化导致的 Redis 中数据不可读问题
- */
-@Configuration
-public class RedisConfig {
-
-    /**
-     * 配置 RedisTemplate
-     * key/hashKey 使用 StringRedisSerializer,value/hashValue 使用 Jackson2JsonRedisSerializer
-     *
-     * @param connectionFactory Redis 连接工厂
-     * @return RedisTemplate
-     */
-    @Bean
-    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
-        RedisTemplate<String, Object> template = new RedisTemplate<>();
-        template.setConnectionFactory(connectionFactory);
-
-        // 配置 JSON 序列化器
-        Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
-        // 启用默认类型,存储时记录类型信息,反序列化时可还原为原始对象
-        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
-                ObjectMapper.DefaultTyping.NON_FINAL);
-        jsonSerializer.setObjectMapper(mapper);
-
-        // key 和 hashKey 使用字符串序列化
-        StringRedisSerializer stringSerializer = new StringRedisSerializer();
-        template.setKeySerializer(stringSerializer);
-        template.setHashKeySerializer(stringSerializer);
-
-        // value 和 hashValue 使用 JSON 序列化
-        template.setValueSerializer(jsonSerializer);
-        template.setHashValueSerializer(jsonSerializer);
-
-        template.afterPropertiesSet();
-        return template;
-    }
-}

+ 0 - 47
code/backend/src/main/java/com/aijiuyi/admin/common/config/WebMvcConfig.java

@@ -1,47 +0,0 @@
-package com.aijiuyi.admin.common.config;
-
-import com.aijiuyi.admin.common.interceptor.TokenInterceptor;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-/**
- * Web MVC 配置类
- * 注册 Token 拦截器和跨域配置
- */
-@Configuration
-public class WebMvcConfig implements WebMvcConfigurer {
-
-    @Autowired
-    private TokenInterceptor tokenInterceptor;
-
-    /**
-     * 注册拦截器
-     * TokenInterceptor 拦截所有请求(/error 路径除外)
-     *
-     * @param registry 拦截器注册表
-     */
-    @Override
-    public void addInterceptors(InterceptorRegistry registry) {
-        registry.addInterceptor(tokenInterceptor)
-                .addPathPatterns("/**")
-                .excludePathPatterns("/error");
-    }
-
-    /**
-     * 配置跨域规则(开发环境允许所有来源)
-     *
-     * @param registry 跨域注册表
-     */
-    @Override
-    public void addCorsMappings(CorsRegistry registry) {
-        registry.addMapping("/**")
-                .allowedOriginPatterns("*")
-                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
-                .allowedHeaders("*")
-                .allowCredentials(true)
-                .maxAge(3600);
-    }
-}

+ 0 - 145
code/backend/src/main/java/com/aijiuyi/admin/common/constant/ResultCode.java

@@ -1,145 +0,0 @@
-package com.aijiuyi.admin.common.constant;
-
-/**
- * 统一返回码枚举
- * 所有接口返回码和错误信息都在此定义,禁止在其他地方硬编码错误信息
- * 新增业务错误码请在对应业务区间追加
- */
-public enum ResultCode {
-
-    // ======================== 通用状态码 ========================
-    /** 操作成功 */
-    SUCCESS(200, "操作成功"),
-    /** 服务器内部错误 */
-    ERROR(500, "服务器内部错误"),
-    /** 参数错误 */
-    PARAM_ERROR(400, "参数错误"),
-    /** 未授权,请登录 */
-    UNAUTHORIZED(401, "未授权,请登录"),
-    /** 权限不足 */
-    FORBIDDEN(403, "权限不足"),
-    /** 资源不存在 */
-    NOT_FOUND(404, "资源不存在"),
-
-    // ======================== 认证/用户相关 1000-1099 ========================
-    /** 用户不存在 */
-    USER_NOT_FOUND(1001, "用户不存在"),
-    /** 用户名或密码错误 */
-    USER_PASSWORD_ERROR(1002, "用户名或密码错误"),
-    /** 用户已被禁用 */
-    USER_DISABLED(1003, "用户已被禁用"),
-    /** Token 已过期 */
-    TOKEN_EXPIRED(1004, "Token 已过期,请重新登录"),
-    /** Token 无效 */
-    TOKEN_INVALID(1005, "Token 无效,请重新登录"),
-    /** 用户名已存在 */
-    USERNAME_EXISTS(1006, "用户名已存在"),
-
-    // ======================== 应用用户相关 1100-1199 ========================
-    /** 应用用户不存在 */
-    APP_USER_NOT_FOUND(1100, "用户不存在"),
-    /** 手机号已注册 */
-    PHONE_EXISTS(1101, "手机号已注册"),
-    /** 应用用户已被禁用 */
-    APP_USER_DISABLED(1102, "用户已被禁用"),
-
-    // ======================== 子用户相关 1200-1299 ========================
-    /** 子用户不存在 */
-    PROFILE_NOT_FOUND(1200, "子用户不存在"),
-    /** 子用户数量超出限制 */
-    PROFILE_LIMIT_EXCEEDED(1201, "子用户数量已达上限"),
-    /** 子用户手机号已存在 */
-    PHONE_PROFILE_EXISTS(1202, "该手机号已存在"),
-    /** 指宽数值超出性别对应范围 */
-    FINGER_WIDTH_RANGE_ERROR(1203, "指宽数值超出该性别允许范围"),
-
-    // ======================== 设备相关 1300-1399 ========================
-    /** 设备不存在 */
-    DEVICE_NOT_FOUND(1300, "设备不存在"),
-    /** 设备已被绑定 */
-    DEVICE_ALREADY_BOUND(1301, "设备已被其他用户绑定"),
-    /** 设备编号已存在 */
-    DEVICE_CODE_EXISTS(1302, "设备编号已存在"),
-    /** 设备序列号已存在 */
-    DEVICE_SERIAL_EXISTS(1303, "设备序列号已存在"),
-    /** 用户已绑定该设备 */
-    DEVICE_USER_ALREADY_BOUND(1304, "该用户已绑定此设备"),
-
-    // ======================== 穴位相关 1400-1499 ========================
-    /** 穴位不存在 */
-    ACUPOINT_NOT_FOUND(1400, "穴位不存在"),
-    /** 穴位编码已存在 */
-    ACUPOINT_CODE_EXISTS(1401, "穴位编码已存在"),
-    /** 穴位名称已存在 */
-    ACUPOINT_NAME_EXISTS(1402, "穴位名称已存在"),
-
-    // ======================== 用户穴位坐标相关 1500-1599 ========================
-    /** 用户穴位坐标记录不存在 */
-    USER_ACUPOINT_NOT_FOUND(1500, "用户穴位坐标记录不存在"),
-    /** 用户穴位坐标生成失败 */
-    USER_ACUPOINT_GENERATE_FAILED(1501, "用户穴位坐标生成失败"),
-
-    // ======================== 艾灸手法相关 1600-1699 ========================
-    /** 艾灸手法不存在 */
-    TECHNIQUE_NOT_FOUND(1600, "艾灸手法不存在"),
-    /** 手法编码已存在 */
-    TECHNIQUE_CODE_EXISTS(1601, "手法编码已存在"),
-    /** 手法名称已存在 */
-    TECHNIQUE_NAME_EXISTS(1602, "手法名称已存在"),
-
-    // ======================== 方案相关 1700-1799 ========================
-    /** 方案不存在 */
-    PLAN_NOT_FOUND(1700, "方案不存在"),
-    /** 方案编码已存在 */
-    PLAN_CODE_EXISTS(1701, "方案编码已存在"),
-    /** 方案名称已存在 */
-    PLAN_NAME_EXISTS(1702, "方案名称已存在"),
-    /** 已发布方案不可删除 */
-    PLAN_CANNOT_DELETE(1703, "已发布或下架方案不可删除"),
-    /** 方案步骤不存在 */
-    PLAN_STEP_NOT_FOUND(1704, "方案步骤不存在"),
-    /** 用户方案不存在 */
-    USER_PLAN_NOT_FOUND(1705, "用户方案不存在"),
-    /** 方案步骤至少保留1个 */
-    PLAN_STEP_MIN_ERROR(1706, "方案步骤至少保留1个"),
-
-    // ======================== 管理员管理相关 1800-1899 ========================
-    /** 管理员不存在 */
-    ADMIN_NOT_FOUND(1800, "管理员不存在"),
-    /** 不能删除超级管理员 */
-    ADMIN_CANNOT_DELETE(1801, "超级管理员不可删除"),
-    /** 不能禁用自身账号 */
-    ADMIN_CANNOT_DISABLE_SELF(1802, "不能禁用当前登录账号"),
-    /** 管理员角色不合法 */
-    ADMIN_ROLE_INVALID(1803, "管理员角色不合法");
-
-    // ======================== 其他业务错误码在此区间之后继续添加 ========================
-
-    /** 返回码 */
-    private final int code;
-    /** 返回信息 */
-    private final String message;
-
-    ResultCode(int code, String message) {
-        this.code = code;
-        this.message = message;
-    }
-
-    /**
-     * 获取返回码
-     *
-     * @return 返回码
-     */
-    public int getCode() {
-        return code;
-    }
-
-    /**
-     * 获取返回信息
-     *
-     * @return 返回信息
-     */
-    public String getMessage() {
-        return message;
-    }
-}

+ 0 - 108
code/backend/src/main/java/com/aijiuyi/admin/common/context/RequestContext.java

@@ -1,108 +0,0 @@
-package com.aijiuyi.admin.common.context;
-
-/**
- * 请求上下文(ThreadLocal)
- * 每个请求线程独立存储:请求ID、当前用户ID、当前用户名、操作者类型
- * 请求开始时由 TokenInterceptor 设置,请求结束时调用 clear() 清理,防止内存泄漏
- */
-public class RequestContext {
-
-    /** 请求唯一ID(UUID,无横线格式) */
-    private static final ThreadLocal<String> REQUEST_ID = new ThreadLocal<>();
-
-    /** 当前登录用户ID */
-    private static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
-
-    /** 当前登录用户名 */
-    private static final ThreadLocal<String> USER_NAME = new ThreadLocal<>();
-
-    /**
-     * 操作者类型(区分管理员和 App 用户)
-     * 1=系统管理员(sys_user),2=App用户(app_user)
-     */
-    private static final ThreadLocal<Integer> USER_TYPE = new ThreadLocal<>();
-
-    /**
-     * 设置请求ID
-     *
-     * @param requestId 请求ID
-     */
-    public static void setRequestId(String requestId) {
-        REQUEST_ID.set(requestId);
-    }
-
-    /**
-     * 获取请求ID
-     *
-     * @return 请求ID
-     */
-    public static String getRequestId() {
-        return REQUEST_ID.get();
-    }
-
-    /**
-     * 设置当前用户ID
-     *
-     * @param userId 用户ID
-     */
-    public static void setUserId(Long userId) {
-        USER_ID.set(userId);
-    }
-
-    /**
-     * 获取当前用户ID
-     *
-     * @return 用户ID
-     */
-    public static Long getUserId() {
-        return USER_ID.get();
-    }
-
-    /**
-     * 设置当前用户名
-     *
-     * @param userName 用户名
-     */
-    public static void setUserName(String userName) {
-        USER_NAME.set(userName);
-    }
-
-    /**
-     * 获取当前用户名
-     *
-     * @return 用户名
-     */
-    public static String getUserName() {
-        return USER_NAME.get();
-    }
-
-    /**
-     * 设置操作者类型
-     * 1=系统管理员(sys_user),2=App用户(app_user)
-     *
-     * @param userType 用户类型
-     */
-    public static void setUserType(Integer userType) {
-        USER_TYPE.set(userType);
-    }
-
-    /**
-     * 获取操作者类型
-     *
-     * @return 用户类型
-     */
-    public static Integer getUserType() {
-        return USER_TYPE.get();
-    }
-
-    /**
-     * 清理当前线程的所有上下文数据(防止内存泄漏)
-     * 在请求结束时由拦截器调用
-     */
-    public static void clear() {
-        REQUEST_ID.remove();
-        USER_ID.remove();
-        USER_NAME.remove();
-        USER_TYPE.remove();
-    }
-}

+ 0 - 89
code/backend/src/main/java/com/aijiuyi/admin/common/entity/Result.java

@@ -1,89 +0,0 @@
-package com.aijiuyi.admin.common.entity;
-
-import com.aijiuyi.admin.common.constant.ResultCode;
-import lombok.Data;
-
-/**
- * 统一接口返回对象
- * 所有 Controller 接口必须返回此对象,禁止直接返回业务对象
- *
- * @param <T> 业务数据类型
- */
-@Data
-public class Result<T> {
-
-    /** 状态码(200=成功,其他=失败,详见 ResultCode) */
-    private int code;
-
-    /** 提示信息 */
-    private String message;
-
-    /** 业务数据 */
-    private T data;
-
-    /**
-     * 操作成功(无数据)
-     *
-     * @param <T> 数据类型
-     * @return Result
-     */
-    public static <T> Result<T> success() {
-        Result<T> result = new Result<>();
-        result.setCode(ResultCode.SUCCESS.getCode());
-        result.setMessage(ResultCode.SUCCESS.getMessage());
-        return result;
-    }
-
-    /**
-     * 操作成功(带数据)
-     *
-     * @param data 业务数据
-     * @param <T>  数据类型
-     * @return Result
-     */
-    public static <T> Result<T> success(T data) {
-        Result<T> result = success();
-        result.setData(data);
-        return result;
-    }
-
-    /**
-     * 操作失败(使用 ResultCode 枚举)
-     *
-     * @param resultCode 错误码枚举
-     * @param <T>        数据类型
-     * @return Result
-     */
-    public static <T> Result<T> error(ResultCode resultCode) {
-        Result<T> result = new Result<>();
-        result.setCode(resultCode.getCode());
-        result.setMessage(resultCode.getMessage());
-        return result;
-    }
-
-    /**
-     * 操作失败(自定义错误码和信息)
-     *
-     * @param code    错误码
-     * @param message 错误信息
-     * @param <T>     数据类型
-     * @return Result
-     */
-    public static <T> Result<T> error(int code, String message) {
-        Result<T> result = new Result<>();
-        result.setCode(code);
-        result.setMessage(message);
-        return result;
-    }
-
-    /**
-     * 操作失败(仅错误信息,错误码默认 500)
-     *
-     * @param message 错误信息
-     * @param <T>     数据类型
-     * @return Result
-     */
-    public static <T> Result<T> error(String message) {
-        return error(ResultCode.ERROR.getCode(), message);
-    }
-}

+ 0 - 57
code/backend/src/main/java/com/aijiuyi/admin/common/entity/SysLog.java

@@ -1,57 +0,0 @@
-package com.aijiuyi.admin.common.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * 系统操作日志实体
- * 对应数据库 sys_log 表,记录所有通过 Controller 的用户操作
- */
-@Data
-@TableName("sys_log")
-public class SysLog {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 请求用户ID */
-    private Long userId;
-
-    /** 请求用户名 */
-    private String userName;
-
-    /** 操作者类型:1=系统管理员(sys_user),2=App用户(app_user) */
-    private Integer userType;
-
-    /** 请求接口路径 */
-    private String requestUrl;
-
-    /** 请求参数(JSON格式) */
-    private String requestParams;
-
-    /** 请求结果(JSON格式) */
-    private String requestResult;
-
-    /** 接口说明(来自 @Log 注解 value 属性) */
-    private String apiDesc;
-
-    /** 操作方式(查询/新增/更新/删除/其他) */
-    private String operationType;
-
-    /** 请求IP */
-    private String requestIp;
-
-    /** 操作模块(来自 @Log 注解 module 属性) */
-    private String operationModule;
-
-    /** 请求唯一ID(同一请求的所有日志共享此ID) */
-    private String requestId;
-
-    /** 创建时间 */
-    private LocalDateTime createTime;
-}

+ 0 - 39
code/backend/src/main/java/com/aijiuyi/admin/common/enums/OperationType.java

@@ -1,39 +0,0 @@
-package com.aijiuyi.admin.common.enums;
-
-/**
- * 操作类型枚举
- * 用于 @Log 注解中标识接口的操作类型,便于日志查询和统计
- */
-public enum OperationType {
-
-    /** 查询操作 */
-    QUERY("查询"),
-
-    /** 新增操作 */
-    INSERT("新增"),
-
-    /** 更新操作 */
-    UPDATE("更新"),
-
-    /** 删除操作 */
-    DELETE("删除"),
-
-    /** 其他操作(登录、导出等) */
-    OTHER("其他");
-
-    /** 操作类型描述 */
-    private final String desc;
-
-    OperationType(String desc) {
-        this.desc = desc;
-    }
-
-    /**
-     * 获取操作类型描述
-     *
-     * @return 描述文字
-     */
-    public String getDesc() {
-        return desc;
-    }
-}

+ 0 - 47
code/backend/src/main/java/com/aijiuyi/admin/common/exception/BusinessException.java

@@ -1,47 +0,0 @@
-package com.aijiuyi.admin.common.exception;
-
-import com.aijiuyi.admin.common.constant.ResultCode;
-import lombok.Getter;
-
-/**
- * 业务异常类
- * 业务逻辑中主动抛出的异常,会被全局异常处理器捕获并返回对应错误信息
- * 示例:throw new BusinessException(ResultCode.USER_NOT_FOUND);
- */
-@Getter
-public class BusinessException extends RuntimeException {
-
-    /** 错误码 */
-    private final int code;
-
-    /**
-     * 通过 ResultCode 枚举抛出异常(推荐方式)
-     *
-     * @param resultCode 错误码枚举
-     */
-    public BusinessException(ResultCode resultCode) {
-        super(resultCode.getMessage());
-        this.code = resultCode.getCode();
-    }
-
-    /**
-     * 通过自定义错误码和信息抛出异常
-     *
-     * @param code    错误码
-     * @param message 错误信息
-     */
-    public BusinessException(int code, String message) {
-        super(message);
-        this.code = code;
-    }
-
-    /**
-     * 通过自定义信息抛出异常(错误码默认 500)
-     *
-     * @param message 错误信息
-     */
-    public BusinessException(String message) {
-        super(message);
-        this.code = ResultCode.ERROR.getCode();
-    }
-}

+ 0 - 91
code/backend/src/main/java/com/aijiuyi/admin/common/exception/GlobalExceptionHandler.java

@@ -1,91 +0,0 @@
-package com.aijiuyi.admin.common.exception;
-
-import com.aijiuyi.admin.common.constant.ResultCode;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.util.LogUtil;
-import org.springframework.validation.BindException;
-import org.springframework.web.bind.MethodArgumentNotValidException;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.bind.annotation.RestControllerAdvice;
-
-import javax.validation.ConstraintViolationException;
-
-/**
- * 全局异常处理器
- * 统一捕获并处理所有 Controller 层异常,返回标准 Result 格式
- */
-@RestControllerAdvice
-public class GlobalExceptionHandler {
-
-    /**
-     * 处理业务异常(主动抛出的可预期异常)
-     *
-     * @param e 业务异常
-     * @return 标准错误响应
-     */
-    @ExceptionHandler(BusinessException.class)
-    public Result<?> handleBusinessException(BusinessException e) {
-        LogUtil.warn(GlobalExceptionHandler.class, "业务异常: code={}, message={}", e.getCode(), e.getMessage());
-        return Result.error(e.getCode(), e.getMessage());
-    }
-
-    /**
-     * 处理请求体参数校验异常(@RequestBody 参数上 @Valid 校验失败)
-     *
-     * @param e 参数校验异常
-     * @return 标准错误响应
-     */
-    @ExceptionHandler(MethodArgumentNotValidException.class)
-    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
-        String message = ResultCode.PARAM_ERROR.getMessage();
-        if (e.getBindingResult().hasFieldErrors()) {
-            message = e.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
-        }
-        LogUtil.warn(GlobalExceptionHandler.class, "参数校验异常: {}", message);
-        return Result.error(ResultCode.PARAM_ERROR.getCode(), message);
-    }
-
-    /**
-     * 处理表单参数绑定异常
-     *
-     * @param e 绑定异常
-     * @return 标准错误响应
-     */
-    @ExceptionHandler(BindException.class)
-    public Result<?> handleBindException(BindException e) {
-        String message = ResultCode.PARAM_ERROR.getMessage();
-        if (e.getBindingResult().hasFieldErrors()) {
-            message = e.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
-        }
-        LogUtil.warn(GlobalExceptionHandler.class, "参数绑定异常: {}", message);
-        return Result.error(ResultCode.PARAM_ERROR.getCode(), message);
-    }
-
-    /**
-     * 处理路径参数/方法参数约束校验异常(@RequestParam/@PathVariable 上 @NotNull 等校验失败)
-     *
-     * @param e 约束违反异常
-     * @return 标准错误响应
-     */
-    @ExceptionHandler(ConstraintViolationException.class)
-    public Result<?> handleConstraintViolationException(ConstraintViolationException e) {
-        String message = e.getConstraintViolations().stream()
-                .findFirst()
-                .map(v -> v.getMessage())
-                .orElse(ResultCode.PARAM_ERROR.getMessage());
-        LogUtil.warn(GlobalExceptionHandler.class, "约束校验异常: {}", message);
-        return Result.error(ResultCode.PARAM_ERROR.getCode(), message);
-    }
-
-    /**
-     * 处理其他未知异常(系统级别异常)
-     *
-     * @param e 未知异常
-     * @return 标准错误响应
-     */
-    @ExceptionHandler(Exception.class)
-    public Result<?> handleException(Exception e) {
-        LogUtil.error(GlobalExceptionHandler.class, "系统未知异常: ", e);
-        return Result.error(ResultCode.ERROR);
-    }
-}

+ 0 - 42
code/backend/src/main/java/com/aijiuyi/admin/common/handler/MyMetaObjectHandler.java

@@ -1,42 +0,0 @@
-package com.aijiuyi.admin.common.handler;
-
-import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
-import org.apache.ibatis.reflection.MetaObject;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
-
-/**
- * MyBatis Plus 自动填充处理器
- * 实体类字段使用 @TableField(fill = FieldFill.INSERT) 标注后,新增时自动填充 createTime
- * 使用 @TableField(fill = FieldFill.INSERT_UPDATE) 标注后,新增和更新时自动填充 updateTime
- */
-@Component
-public class MyMetaObjectHandler implements MetaObjectHandler {
-
-    /**
-     * 新增时自动填充
-     * 填充字段:createTime(创建时间)、updateTime(更新时间)
-     *
-     * @param metaObject 元对象
-     */
-    @Override
-    public void insertFill(MetaObject metaObject) {
-        // 新增时自动填充创建时间
-        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
-        // 新增时也填充更新时间
-        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
-    }
-
-    /**
-     * 更新时自动填充
-     * 填充字段:updateTime(更新时间)
-     *
-     * @param metaObject 元对象
-     */
-    @Override
-    public void updateFill(MetaObject metaObject) {
-        // 更新时自动更新时间
-        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
-    }
-}

+ 0 - 162
code/backend/src/main/java/com/aijiuyi/admin/common/interceptor/TokenInterceptor.java

@@ -1,162 +0,0 @@
-package com.aijiuyi.admin.common.interceptor;
-
-import com.aijiuyi.admin.common.annotation.NoAuth;
-import com.aijiuyi.admin.common.config.AuthProperties;
-import com.aijiuyi.admin.common.constant.ResultCode;
-import com.aijiuyi.admin.common.context.RequestContext;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.util.LogUtil;
-import com.aijiuyi.admin.common.util.RedisUtil;
-import com.aijiuyi.admin.common.util.TokenUtil;
-import com.alibaba.fastjson.JSON;
-import io.jsonwebtoken.Claims;
-import org.slf4j.MDC;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.springframework.util.AntPathMatcher;
-import org.springframework.util.StringUtils;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.HandlerInterceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.PrintWriter;
-import java.util.UUID;
-
-/**
- * Token 鉴权拦截器
- * 所有请求在进入 Controller 之前经过此拦截器处理:
- * 1. 生成唯一请求ID,存入 ThreadLocal 和 MDC(使所有日志自动携带请求ID)
- * 2. 验证 Token 有效性(白名单、@NoAuth 注解的接口跳过验证)
- * 3. 解析 Token,将用户信息存入 ThreadLocal
- */
-@Component
-public class TokenInterceptor implements HandlerInterceptor {
-
-    /** Token 在请求头中的 key */
-    private static final String TOKEN_HEADER = "Authorization";
-    /** Redis 中 Token 的 key 前缀 */
-    public static final String REDIS_TOKEN_KEY_PREFIX = "token:";
-    /** Ant 路径匹配器 */
-    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
-    /** MDC 中请求ID的 key */
-    private static final String MDC_REQUEST_ID = "requestId";
-
-    @Autowired
-    private RedisUtil redisUtil;
-
-    @Autowired
-    private AuthProperties authProperties;
-
-    /**
-     * 请求进入 Controller 前处理
-     * 生成请求ID,验证 Token,解析用户信息
-     *
-     * @param request  请求
-     * @param response 响应
-     * @param handler  处理器
-     * @return true=放行,false=拦截
-     */
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        // 生成唯一请求ID(UUID去横线)
-        String requestId = UUID.randomUUID().toString().replace("-", "");
-        // 存入 ThreadLocal,供业务代码使用
-        RequestContext.setRequestId(requestId);
-        // 存入 MDC,使所有 SLF4J 日志(包括 SQL 日志)自动携带请求ID
-        MDC.put(MDC_REQUEST_ID, requestId);
-
-        // 非 Handler 方法(如静态资源)直接放行
-        if (!(handler instanceof HandlerMethod)) {
-            return true;
-        }
-
-        HandlerMethod handlerMethod = (HandlerMethod) handler;
-
-        // 检查方法或类上是否有 @NoAuth 注解,有则跳过 Token 验证
-        if (handlerMethod.hasMethodAnnotation(NoAuth.class)
-                || handlerMethod.getBeanType().isAnnotationPresent(NoAuth.class)) {
-            return true;
-        }
-
-        // 检查是否在配置文件的白名单中
-        String requestUri = request.getRequestURI();
-        for (String pattern : authProperties.getWhiteList()) {
-            if (StringUtils.hasText(pattern) && PATH_MATCHER.match(pattern.trim(), requestUri)) {
-                return true;
-            }
-        }
-
-        // 从请求头获取 Token
-        String token = request.getHeader(TOKEN_HEADER);
-        if (!StringUtils.hasText(token)) {
-            writeErrorResponse(response, ResultCode.UNAUTHORIZED);
-            return false;
-        }
-
-        // 去掉 Bearer 前缀
-        if (token.startsWith("Bearer ")) {
-            token = token.substring(7);
-        }
-
-        // 验证 Token 是否在 Redis 中存在(已退出登录的 Token 会被删除)
-        String redisKey = REDIS_TOKEN_KEY_PREFIX + token;
-        if (!redisUtil.hasKey(redisKey)) {
-            writeErrorResponse(response, ResultCode.TOKEN_INVALID);
-            return false;
-        }
-
-        // 解析 Token,获取用户信息
-        Claims claims = TokenUtil.parseToken(token);
-        if (claims == null) {
-            writeErrorResponse(response, ResultCode.TOKEN_INVALID);
-            return false;
-        }
-
-        // 将用户信息存入 ThreadLocal,供 Service 层使用
-        Long userId = ((Number) claims.get("userId")).longValue();
-        String username = (String) claims.get("username");
-        // 从 JWT Claims 中读取 userType(兼容旧 token 默认为 3=管理员)
-        Integer userType = claims.get("userType") != null
-                ? ((Number) claims.get("userType")).intValue()
-                : 3;
-        RequestContext.setUserId(userId);
-        RequestContext.setUserName(username);
-        RequestContext.setUserType(userType);
-
-        LogUtil.debug(TokenInterceptor.class, "用户[{}]请求接口[{}]", username, requestUri);
-        return true;
-    }
-
-    /**
-     * 请求完成后清理 ThreadLocal 和 MDC,防止内存泄漏
-     *
-     * @param request  请求
-     * @param response 响应
-     * @param handler  处理器
-     * @param ex       异常(如有)
-     */
-    @Override
-    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
-                                Object handler, Exception ex) {
-        // 清理 ThreadLocal
-        RequestContext.clear();
-        // 清理 MDC
-        MDC.clear();
-    }
-
-    /**
-     * 向客户端写入统一的错误响应
-     *
-     * @param response   响应
-     * @param resultCode 错误码
-     */
-    private void writeErrorResponse(HttpServletResponse response, ResultCode resultCode) throws Exception {
-        response.setContentType("application/json;charset=UTF-8");
-        response.setStatus(HttpServletResponse.SC_OK);
-        try (PrintWriter writer = response.getWriter()) {
-            writer.write(JSON.toJSONString(Result.error(resultCode)));
-            writer.flush();
-        }
-    }
-}

+ 0 - 47
code/backend/src/main/java/com/aijiuyi/admin/common/service/SysLogService.java

@@ -1,47 +0,0 @@
-package com.aijiuyi.admin.common.service;
-
-import com.aijiuyi.admin.common.entity.SysLog;
-import com.aijiuyi.admin.controller.dto.SysLogQueryDTO;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * 系统日志 Service 接口
- * 提供操作日志的保存、分页查询和全量导出功能
- */
-public interface SysLogService {
-
-    /**
-     * 异步保存操作日志到数据库
-     * 由 LogAspect 切面调用,不影响主业务流程
-     *
-     * @param sysLog 操作日志对象
-     */
-    void saveLog(SysLog sysLog);
-
-    /**
-     * 分页查询操作日志列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<SysLog> pageList(SysLogQueryDTO queryDTO);
-
-    /**
-     * 查询全部操作日志(用于 Excel 导出)
-     *
-     * @param queryDTO 查询条件(不含分页参数)
-     * @return 日志列表
-     */
-    List<SysLog> listAll(SysLogQueryDTO queryDTO);
-
-    /**
-     * 获取系统管理员选项列表(用于前端操作人下拉框)
-     *
-     * @return id 和 nickname/username 组成的选项列表
-     */
-    List<Map<String, Object>> getSysUserOptions();
-}
-

+ 0 - 102
code/backend/src/main/java/com/aijiuyi/admin/common/service/impl/SysLogServiceImpl.java

@@ -1,102 +0,0 @@
-package com.aijiuyi.admin.common.service.impl;
-
-import com.aijiuyi.admin.common.entity.SysLog;
-import com.aijiuyi.admin.common.service.SysLogService;
-import com.aijiuyi.admin.common.util.LogUtil;
-import com.aijiuyi.admin.controller.dto.SysLogQueryDTO;
-import com.aijiuyi.admin.entity.AppUser;
-import com.aijiuyi.admin.mapper.AppUserMapper;
-import com.aijiuyi.admin.mapper.SysLogMapper;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 系统日志 Service 实现类
- * saveLog 方法使用 @Async 异步执行,不阻塞主请求线程
- */
-@Service
-public class SysLogServiceImpl implements SysLogService {
-
-    @Autowired
-    private SysLogMapper sysLogMapper;
-
-    @Autowired
-    private AppUserMapper appUserMapper;
-
-    /**
-     * 异步保存操作日志
-     * 使用 asyncExecutor 线程池执行,与主请求线程解耦
-     *
-     * @param sysLog 操作日志对象
-     */
-    @Override
-    @Async("asyncExecutor")
-    public void saveLog(SysLog sysLog) {
-        try {
-            sysLogMapper.insert(sysLog);
-            LogUtil.debug(SysLogServiceImpl.class, "操作日志保存成功, requestId={}", sysLog.getRequestId());
-        } catch (Exception e) {
-            LogUtil.error(SysLogServiceImpl.class, "操作日志保存失败: {}", e.getMessage());
-        }
-    }
-
-    /**
-     * 分页查询操作日志列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @Override
-    public IPage<SysLog> pageList(SysLogQueryDTO queryDTO) {
-        Page<SysLog> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
-        return sysLogMapper.selectLogPage(page, queryDTO);
-    }
-
-    /**
-     * 查询全部操作日志(用于 Excel 导出,不分页)
-     *
-     * @param queryDTO 查询条件
-     * @return 日志列表
-     */
-    @Override
-    public List<SysLog> listAll(SysLogQueryDTO queryDTO) {
-        return sysLogMapper.selectLogList(queryDTO);
-    }
-
-    /**
-     * 获取用户选项列表(用于前端操作人下拉框)
-     * 返回所有未删除的用户的 id、username 和 nickname
-     *
-     * @return 选项列表,每项含 id、label(nickname 或 username)
-     */
-    @Override
-    public List<Map<String, Object>> getSysUserOptions() {
-        List<AppUser> users = appUserMapper.selectList(
-                new LambdaQueryWrapper<AppUser>()
-                        .select(AppUser::getId, AppUser::getUsername, AppUser::getNickname)
-                        .orderByAsc(AppUser::getId)
-        );
-        List<Map<String, Object>> options = new ArrayList<>();
-        for (AppUser user : users) {
-            Map<String, Object> option = new HashMap<>();
-            option.put("id", user.getId());
-            // 优先展示昵称,无昵称则展示用户名
-            String label = (user.getNickname() != null && !user.getNickname().isEmpty())
-                    ? user.getNickname() : user.getUsername();
-            option.put("label", label);
-            option.put("username", user.getUsername());
-            options.add(option);
-        }
-        return options;
-    }
-}
-

+ 0 - 275
code/backend/src/main/java/com/aijiuyi/admin/common/util/AcupointLocationParser.java

@@ -1,275 +0,0 @@
-package com.aijiuyi.admin.common.util;
-
-import com.aijiuyi.admin.entity.Acupoint;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * 从穴位定位原文中提取可计算的相对坐标参数。
- */
-public final class AcupointLocationParser {
-
-    public static final String STATUS_PARSED = "parsed";
-    public static final String STATUS_REVIEW_REQUIRED = "review_required";
-    public static final String STATUS_UNSUPPORTED = "unsupported";
-
-    public static final String CONFIDENCE_HIGH = "high";
-    public static final String CONFIDENCE_MEDIUM = "medium";
-    public static final String CONFIDENCE_LOW = "low";
-
-    private static final Pattern MIDLINE_OFFSET =
-            Pattern.compile("(?:前|后|头)?正中线旁开\\s*([0-9]+(?:\\.[0-9]+)?)\\s*寸");
-    private static final Pattern GENERAL_OFFSET =
-            Pattern.compile("旁开\\s*([0-9]+(?:\\.[0-9]+)?)\\s*寸");
-    private static final Pattern UNSUPPORTED_TOUCH =
-            Pattern.compile("凹陷|肌腱|骨间|缝中|连线|赤白肉际|脉搏动|鼻唇沟|瞳孔|发际|骨间隙");
-    private static final Pattern ABDOMEN_REFERENCE =
-            Pattern.compile("脐中|脐上|脐下|剑胸结合|胸剑联合|肋间隙|乳头");
-
-    private AcupointLocationParser() {
-    }
-
-    public static ParseResult parse(Acupoint acupoint) {
-        String text = mergeText(acupoint);
-        ParseResult result = new ParseResult();
-
-        if (text.length() == 0) {
-            result.setParseStatus(STATUS_REVIEW_REQUIRED);
-            result.setParseConfidence(CONFIDENCE_LOW);
-            result.setParseMessage("缺少穴位定位描述,无法自动解析");
-            result.setSide(parseSide(acupoint, "", null));
-            return result;
-        }
-
-        String yTerm = AcupointYReference.inferTerm(text);
-        AcupointYReference.Rule yRule = AcupointYReference.getRule(yTerm);
-        BigDecimal lateralOffset = parseLateralOffset(text);
-        Integer side = parseSide(acupoint, text, lateralOffset);
-
-        result.setYReferenceTerm(yTerm);
-        result.setLateralOffset(lateralOffset);
-        result.setSide(side);
-
-        boolean hasY = yRule != null;
-        boolean needsLateral = Integer.valueOf(2).equals(side)
-                || Integer.valueOf(3).equals(side)
-                || Integer.valueOf(4).equals(side);
-        if (isBaliao(acupoint)) {
-            needsLateral = false;
-        }
-        boolean hasLateral = lateralOffset != null && lateralOffset.compareTo(BigDecimal.ZERO) > 0;
-        boolean touchOnly = UNSUPPORTED_TOUCH.matcher(text).find();
-        boolean abdomenReference = ABDOMEN_REFERENCE.matcher(text).find();
-
-        List<String> messages = new ArrayList<String>();
-        if (hasY) {
-            result.setNormalizedY(yRule.getNormalizedY());
-            result.setLongitudeOffset(yRule.getCunOffset());
-            messages.add("已解析Y轴术语:" + yTerm);
-        }
-        if (hasLateral) {
-            messages.add("已解析横向旁开:" + lateralOffset.toPlainString() + "寸");
-        }
-        if (touchOnly) {
-            messages.add("包含凹陷/肌腱/骨间/连线等触诊描述,需人工复核精确点位");
-        }
-        if (abdomenReference && !hasY) {
-            messages.add("包含胸腹体表标志,但当前用户模型缺少可稳定换算为大椎相对Y轴的基准");
-        }
-
-        if (hasY && (!needsLateral || hasLateral)) {
-            result.setParseStatus(STATUS_PARSED);
-            result.setParseConfidence(touchOnly ? CONFIDENCE_MEDIUM : CONFIDENCE_HIGH);
-        } else if (hasY) {
-            result.setParseStatus(STATUS_REVIEW_REQUIRED);
-            result.setParseConfidence(CONFIDENCE_MEDIUM);
-            messages.add("已找到Y轴基准,但缺少横向旁开距离");
-        } else if (hasLateral) {
-            result.setParseStatus(STATUS_REVIEW_REQUIRED);
-            result.setParseConfidence(CONFIDENCE_MEDIUM);
-            messages.add("已找到横向旁开距离,但缺少可计算Y轴基准");
-        } else {
-            result.setParseStatus(touchOnly || abdomenReference ? STATUS_REVIEW_REQUIRED : STATUS_UNSUPPORTED);
-            result.setParseConfidence(CONFIDENCE_LOW);
-            if (messages.isEmpty()) {
-                messages.add("未识别到可稳定计算的Y轴基准和横向旁开距离");
-            }
-        }
-
-        result.setParseMessage(joinMessages(messages));
-        return result;
-    }
-
-    private static String mergeText(Acupoint acupoint) {
-        if (acupoint == null) {
-            return "";
-        }
-        StringBuilder builder = new StringBuilder();
-        append(builder, acupoint.getLocationDescription());
-        append(builder, acupoint.getRelativePosition());
-        append(builder, acupoint.getAcupointMethod());
-        append(builder, acupoint.getBodyPart());
-        append(builder, acupoint.getBodyRegion());
-        return builder.toString().trim();
-    }
-
-    private static void append(StringBuilder builder, String value) {
-        if (value != null && value.trim().length() > 0) {
-            if (builder.length() > 0) {
-                builder.append(' ');
-            }
-            builder.append(value.trim());
-        }
-    }
-
-    private static BigDecimal parseLateralOffset(String text) {
-        Matcher midline = MIDLINE_OFFSET.matcher(text);
-        if (midline.find()) {
-            return new BigDecimal(midline.group(1));
-        }
-        Matcher general = GENERAL_OFFSET.matcher(text);
-        if (general.find()) {
-            return new BigDecimal(general.group(1));
-        }
-        return null;
-    }
-
-    private static Integer parseSide(Acupoint acupoint, String text, BigDecimal lateralOffset) {
-        if (text != null) {
-            if (text.contains("双侧") || text.contains("两侧") || text.contains("左右")) {
-                return 4;
-            }
-            if (text.contains("左") && !text.contains("右")) {
-                return 2;
-            }
-            if (text.contains("右") && !text.contains("左")) {
-                return 3;
-            }
-        }
-        String singleDouble = acupoint == null ? null : acupoint.getSingleDouble();
-        if (singleDouble != null && singleDouble.contains("双")) {
-            return 4;
-        }
-        if (singleDouble != null && singleDouble.contains("单")) {
-            return 1;
-        }
-        Integer currentSide = acupoint == null ? null : acupoint.getSide();
-        if (currentSide != null && currentSide > 1 && currentSide <= 4) {
-            return currentSide;
-        }
-        String meridian = acupoint == null ? null : acupoint.getMeridian();
-        if ("督脉".equals(meridian) || "任脉".equals(meridian)) {
-            return 1;
-        }
-        if (lateralOffset != null && lateralOffset.compareTo(BigDecimal.ZERO) > 0) {
-            return 4;
-        }
-        if (currentSide != null && currentSide >= 1 && currentSide <= 4) {
-            return currentSide;
-        }
-        return 1;
-    }
-
-    private static boolean isBaliao(Acupoint acupoint) {
-        if (acupoint == null) {
-            return false;
-        }
-        String code = acupoint.getAcupointCode();
-        if ("pangguang-31".equals(code) || "pangguang-32".equals(code)
-                || "pangguang-33".equals(code) || "pangguang-34".equals(code)) {
-            return true;
-        }
-        String name = acupoint.getName();
-        return "上髎".equals(name) || "次髎".equals(name) || "中髎".equals(name) || "下髎".equals(name);
-    }
-
-    private static String joinMessages(List<String> messages) {
-        StringBuilder builder = new StringBuilder();
-        for (String message : messages) {
-            if (builder.length() > 0) {
-                builder.append(";");
-            }
-            builder.append(message);
-        }
-        return builder.toString();
-    }
-
-    public static class ParseResult {
-        private String yReferenceTerm;
-        private BigDecimal normalizedY;
-        private BigDecimal longitudeOffset;
-        private BigDecimal lateralOffset;
-        private Integer side;
-        private String parseStatus;
-        private String parseConfidence;
-        private String parseMessage;
-
-        public String getYReferenceTerm() {
-            return yReferenceTerm;
-        }
-
-        public void setYReferenceTerm(String yReferenceTerm) {
-            this.yReferenceTerm = yReferenceTerm;
-        }
-
-        public BigDecimal getNormalizedY() {
-            return normalizedY;
-        }
-
-        public void setNormalizedY(BigDecimal normalizedY) {
-            this.normalizedY = normalizedY;
-        }
-
-        public BigDecimal getLongitudeOffset() {
-            return longitudeOffset;
-        }
-
-        public void setLongitudeOffset(BigDecimal longitudeOffset) {
-            this.longitudeOffset = longitudeOffset;
-        }
-
-        public BigDecimal getLateralOffset() {
-            return lateralOffset;
-        }
-
-        public void setLateralOffset(BigDecimal lateralOffset) {
-            this.lateralOffset = lateralOffset;
-        }
-
-        public Integer getSide() {
-            return side;
-        }
-
-        public void setSide(Integer side) {
-            this.side = side;
-        }
-
-        public String getParseStatus() {
-            return parseStatus;
-        }
-
-        public void setParseStatus(String parseStatus) {
-            this.parseStatus = parseStatus;
-        }
-
-        public String getParseConfidence() {
-            return parseConfidence;
-        }
-
-        public void setParseConfidence(String parseConfidence) {
-            this.parseConfidence = parseConfidence;
-        }
-
-        public String getParseMessage() {
-            return parseMessage;
-        }
-
-        public void setParseMessage(String parseMessage) {
-            this.parseMessage = parseMessage;
-        }
-    }
-}

+ 0 - 216
code/backend/src/main/java/com/aijiuyi/admin/common/util/AcupointYReference.java

@@ -1,216 +0,0 @@
-package com.aijiuyi.admin.common.util;
-
-import com.aijiuyi.admin.controller.dto.YReferenceOptionDTO;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * 穴位Y轴医学定位术语映射。
- *
- * <p>统一约定:Y轴向下为正。C7为0,S4为1,胸腰骶段使用C7-S4脊柱长度按
- * Panjabi归一化系数换算;少数“直上N寸”术语使用最终指寸换算。</p>
- */
-public final class AcupointYReference {
-
-    private static final Pattern C7_UP_2_CUN =
-            Pattern.compile("第7颈椎棘突直上2寸");
-    private static final Pattern C7 =
-            Pattern.compile("(?:横平)?第7颈椎棘突(?:下)?");
-    private static final Pattern THORACIC =
-            Pattern.compile("第([1-9]|1[0-2])胸椎棘突下");
-    private static final Pattern LUMBAR =
-            Pattern.compile("第([1-5])腰椎棘突下");
-    private static final Pattern SACRAL_CENTER =
-            Pattern.compile("正对第([1-4])骶后孔中?");
-    private static final Pattern SACRAL_LEVEL =
-            Pattern.compile("横平第([1-4])骶后孔");
-
-    private static final Map<String, Rule> RULES;
-
-    static {
-        Map<String, Rule> rules = new LinkedHashMap<>();
-
-        addNormalized(rules, "第7颈椎棘突", "0.0000");
-        addNormalized(rules, "第7颈椎棘突下", "0.0000");
-        addCunOffset(rules, "第7颈椎棘突直上2寸", "-2.00");
-
-        addNormalized(rules, "第1胸椎棘突下", "0.0340");
-        addNormalized(rules, "第2胸椎棘突下", "0.0780");
-        addNormalized(rules, "第3胸椎棘突下", "0.1200");
-        addNormalized(rules, "第4胸椎棘突下", "0.1620");
-        addNormalized(rules, "第5胸椎棘突下", "0.2030");
-        addNormalized(rules, "第6胸椎棘突下", "0.2430");
-        addNormalized(rules, "第7胸椎棘突下", "0.2830");
-        addNormalized(rules, "第8胸椎棘突下", "0.3220");
-        addNormalized(rules, "第9胸椎棘突下", "0.3610");
-        addNormalized(rules, "第10胸椎棘突下", "0.4000");
-        addNormalized(rules, "第11胸椎棘突下", "0.4380");
-        addNormalized(rules, "第12胸椎棘突下", "0.4760");
-
-        addNormalized(rules, "第1腰椎棘突下", "0.5140");
-        addNormalized(rules, "第2腰椎棘突下", "0.5520");
-        addNormalized(rules, "第3腰椎棘突下", "0.5900");
-        addNormalized(rules, "第4腰椎棘突下", "0.6280");
-        addNormalized(rules, "第5腰椎棘突下", "0.6660");
-
-        addSacral(rules, 1, "0.7500");
-        addSacral(rules, 2, "0.8340");
-        addSacral(rules, 3, "0.9170");
-        addSacral(rules, 4, "1.0000");
-
-        RULES = Collections.unmodifiableMap(rules);
-    }
-
-    private AcupointYReference() {
-    }
-
-    public static List<YReferenceOptionDTO> listOptions() {
-        List<YReferenceOptionDTO> list = new ArrayList<>();
-        for (Map.Entry<String, Rule> entry : RULES.entrySet()) {
-            Rule rule = entry.getValue();
-            YReferenceOptionDTO dto = new YReferenceOptionDTO();
-            dto.setValue(entry.getKey());
-            dto.setLabel(entry.getKey());
-            dto.setNormalizedY(rule.getNormalizedY());
-            dto.setCunOffset(rule.getCunOffset());
-            dto.setDescription(rule.getDescription());
-            list.add(dto);
-        }
-        return list;
-    }
-
-    public static boolean isSupported(String term) {
-        return getRule(term) != null;
-    }
-
-    public static Rule getRule(String term) {
-        String normalizedTerm = normalizeTerm(term);
-        return normalizedTerm == null ? null : RULES.get(normalizedTerm);
-    }
-
-    public static String normalizeTerm(String term) {
-        if (term == null || term.trim().isEmpty()) {
-            return null;
-        }
-        String trimmed = term.trim();
-        if (RULES.containsKey(trimmed)) {
-            return trimmed;
-        }
-        return inferTerm(trimmed);
-    }
-
-    /**
-     * 从 Excel 的“穴位定位”原文中提取可计算的Y轴术语。
-     */
-    public static String inferTerm(String locationDescription) {
-        if (locationDescription == null || locationDescription.trim().isEmpty()) {
-            return null;
-        }
-        String text = locationDescription.trim();
-
-        if (C7_UP_2_CUN.matcher(text).find()) {
-            return "第7颈椎棘突直上2寸";
-        }
-
-        Matcher sacralCenter = SACRAL_CENTER.matcher(text);
-        if (sacralCenter.find()) {
-            return "正对第" + sacralCenter.group(1) + "骶后孔中";
-        }
-
-        Matcher sacralLevel = SACRAL_LEVEL.matcher(text);
-        if (sacralLevel.find()) {
-            return "横平第" + sacralLevel.group(1) + "骶后孔";
-        }
-
-        Matcher thoracic = THORACIC.matcher(text);
-        if (thoracic.find()) {
-            return "第" + thoracic.group(1) + "胸椎棘突下";
-        }
-
-        Matcher lumbar = LUMBAR.matcher(text);
-        if (lumbar.find()) {
-            return "第" + lumbar.group(1) + "腰椎棘突下";
-        }
-
-        Matcher c7 = C7.matcher(text);
-        if (c7.find()) {
-            return text.contains("棘突下") ? "第7颈椎棘突下" : "第7颈椎棘突";
-        }
-
-        return null;
-    }
-
-    /**
-     * 按用户体型计算Y坐标(mm)。
-     *
-     * @return 无法计算时返回 null
-     */
-    public static BigDecimal calculateY(String term, BigDecimal spineLengthCm, BigDecimal finalCunMm) {
-        Rule rule = getRule(term);
-        if (rule == null) {
-            return null;
-        }
-        if (rule.getNormalizedY() != null) {
-            if (rule.getNormalizedY().compareTo(BigDecimal.ZERO) == 0) {
-                return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
-            }
-            if (spineLengthCm == null || spineLengthCm.compareTo(BigDecimal.ZERO) <= 0) {
-                return null;
-            }
-            return spineLengthCm.multiply(BigDecimal.TEN)
-                    .multiply(rule.getNormalizedY())
-                    .setScale(2, RoundingMode.HALF_UP);
-        }
-        if (rule.getCunOffset() != null && finalCunMm != null) {
-            return rule.getCunOffset().multiply(finalCunMm).setScale(2, RoundingMode.HALF_UP);
-        }
-        return null;
-    }
-
-    private static void addNormalized(Map<String, Rule> rules, String term, String normalizedY) {
-        BigDecimal value = new BigDecimal(normalizedY);
-        rules.put(term, new Rule(value, null, "Y = 脊柱长度(C7-S4) × " + value.toPlainString()));
-    }
-
-    private static void addCunOffset(Map<String, Rule> rules, String term, String cunOffset) {
-        BigDecimal value = new BigDecimal(cunOffset);
-        rules.put(term, new Rule(null, value, "Y = " + value.toPlainString() + " × 最终指寸"));
-    }
-
-    private static void addSacral(Map<String, Rule> rules, int index, String normalizedY) {
-        addNormalized(rules, "横平第" + index + "骶后孔", normalizedY);
-        addNormalized(rules, "正对第" + index + "骶后孔中", normalizedY);
-    }
-
-    public static class Rule {
-        private final BigDecimal normalizedY;
-        private final BigDecimal cunOffset;
-        private final String description;
-
-        private Rule(BigDecimal normalizedY, BigDecimal cunOffset, String description) {
-            this.normalizedY = normalizedY;
-            this.cunOffset = cunOffset;
-            this.description = description;
-        }
-
-        public BigDecimal getNormalizedY() {
-            return normalizedY;
-        }
-
-        public BigDecimal getCunOffset() {
-            return cunOffset;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-    }
-}

+ 0 - 57
code/backend/src/main/java/com/aijiuyi/admin/common/util/IpUtil.java

@@ -1,57 +0,0 @@
-package com.aijiuyi.admin.common.util;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * IP 地址获取工具类
- * 支持从 Nginx、Apache、CDN 等代理场景下获取真实客户端 IP
- */
-public class IpUtil {
-
-    /**
-     * 从请求中获取真实客户端 IP 地址
-     * 按优先级依次从以下请求头获取:
-     * X-Forwarded-For > Proxy-Client-IP > WL-Proxy-Client-IP >
-     * HTTP_CLIENT_IP > HTTP_X_FORWARDED_FOR > remoteAddr
-     * 若有多个 IP(经过多层代理),取第一个非 unknown 的 IP
-     *
-     * @param request HTTP 请求
-     * @return 客户端真实 IP 地址
-     */
-    public static String getIpAddr(HttpServletRequest request) {
-        String ip = request.getHeader("X-Forwarded-For");
-        if (isBlankOrUnknown(ip)) {
-            ip = request.getHeader("Proxy-Client-IP");
-        }
-        if (isBlankOrUnknown(ip)) {
-            ip = request.getHeader("WL-Proxy-Client-IP");
-        }
-        if (isBlankOrUnknown(ip)) {
-            ip = request.getHeader("HTTP_CLIENT_IP");
-        }
-        if (isBlankOrUnknown(ip)) {
-            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
-        }
-        if (isBlankOrUnknown(ip)) {
-            ip = request.getHeader("X-Real-IP");
-        }
-        if (isBlankOrUnknown(ip)) {
-            ip = request.getRemoteAddr();
-        }
-        // 多级代理时,X-Forwarded-For 可能是 "ip1,ip2,ip3",取第一个有效IP
-        if (ip != null && ip.contains(",")) {
-            ip = ip.split(",")[0].trim();
-        }
-        return ip;
-    }
-
-    /**
-     * 判断 IP 字符串是否为空或 unknown
-     *
-     * @param ip IP 字符串
-     * @return true=为空或unknown,false=有效IP
-     */
-    private static boolean isBlankOrUnknown(String ip) {
-        return ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip);
-    }
-}

+ 0 - 97
code/backend/src/main/java/com/aijiuyi/admin/common/util/LogUtil.java

@@ -1,97 +0,0 @@
-package com.aijiuyi.admin.common.util;
-
-import com.aijiuyi.admin.common.context.RequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * 统一日志工具类
- * 封装 SLF4J,所有日志自动携带请求ID(格式:[requestId:xxx] 原始消息)
- * 支持 info、debug、warn、error 四种级别
- *
- * 使用示例:
- *   LogUtil.info(UserService.class, "查询用户: userId={}", userId);
- *   LogUtil.error(UserService.class, "保存失败", e);
- */
-public class LogUtil {
-
-    /**
-     * 获取指定类的 Logger
-     *
-     * @param clazz 调用类
-     * @return Logger 实例
-     */
-    private static Logger getLogger(Class<?> clazz) {
-        return LoggerFactory.getLogger(clazz);
-    }
-
-    /**
-     * 构建带请求ID前缀的日志消息
-     * 若无请求ID(如定时任务),则直接返回原始消息
-     *
-     * @param message 原始消息
-     * @return 带请求ID前缀的消息
-     */
-    private static String buildMessage(String message) {
-        String requestId = RequestContext.getRequestId();
-        if (requestId != null && !requestId.isEmpty()) {
-            return "[requestId:" + requestId + "] " + message;
-        }
-        return message;
-    }
-
-    /**
-     * INFO 级别日志
-     *
-     * @param clazz   调用类
-     * @param message 日志消息(支持 {} 占位符)
-     * @param args    占位符参数
-     */
-    public static void info(Class<?> clazz, String message, Object... args) {
-        getLogger(clazz).info(buildMessage(message), args);
-    }
-
-    /**
-     * DEBUG 级别日志
-     *
-     * @param clazz   调用类
-     * @param message 日志消息(支持 {} 占位符)
-     * @param args    占位符参数
-     */
-    public static void debug(Class<?> clazz, String message, Object... args) {
-        getLogger(clazz).debug(buildMessage(message), args);
-    }
-
-    /**
-     * WARN 级别日志(警告,不影响业务但需要关注)
-     *
-     * @param clazz   调用类
-     * @param message 日志消息(支持 {} 占位符)
-     * @param args    占位符参数
-     */
-    public static void warn(Class<?> clazz, String message, Object... args) {
-        getLogger(clazz).warn(buildMessage(message), args);
-    }
-
-    /**
-     * ERROR 级别日志(错误,有占位符参数)
-     *
-     * @param clazz   调用类
-     * @param message 日志消息(支持 {} 占位符)
-     * @param args    占位符参数
-     */
-    public static void error(Class<?> clazz, String message, Object... args) {
-        getLogger(clazz).error(buildMessage(message), args);
-    }
-
-    /**
-     * ERROR 级别日志(错误,带异常堆栈)
-     *
-     * @param clazz   调用类
-     * @param message 日志消息
-     * @param t       异常对象
-     */
-    public static void error(Class<?> clazz, String message, Throwable t) {
-        getLogger(clazz).error(buildMessage(message), t);
-    }
-}

+ 0 - 327
code/backend/src/main/java/com/aijiuyi/admin/common/util/RedisUtil.java

@@ -1,327 +0,0 @@
-package com.aijiuyi.admin.common.util;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Component;
-import org.springframework.util.CollectionUtils;
-
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Redis 统一操作工具类
- * 封装 RedisTemplate,提供 String、Hash、List、Set 等常用数据结构的操作方法
- *
- * 使用示例:
- *   redisUtil.set("key", "value", 3600); // 设置一个小时过期的字符串
- *   redisUtil.get("key");                // 获取值
- */
-@Component
-public class RedisUtil {
-
-    @Autowired
-    private RedisTemplate<String, Object> redisTemplate;
-
-    // ========================= 通用操作 =========================
-
-    /**
-     * 设置 key 的过期时间
-     *
-     * @param key  键
-     * @param time 过期时间(秒),负数或0表示永不过期
-     * @return true=设置成功,false=key不存在或设置失败
-     */
-    public boolean expire(String key, long time) {
-        if (time > 0) {
-            return Boolean.TRUE.equals(redisTemplate.expire(key, time, TimeUnit.SECONDS));
-        }
-        return false;
-    }
-
-    /**
-     * 获取 key 的剩余过期时间
-     *
-     * @param key 键
-     * @return 剩余过期时间(秒),-1表示永不过期,-2表示key不存在
-     */
-    public long getExpire(String key) {
-        Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
-        return expire == null ? -2 : expire;
-    }
-
-    /**
-     * 判断 key 是否存在
-     *
-     * @param key 键
-     * @return true=存在,false=不存在
-     */
-    public boolean hasKey(String key) {
-        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
-    }
-
-    /**
-     * 删除一个或多个 key
-     *
-     * @param keys 要删除的 key(可以传多个)
-     */
-    public void del(String... keys) {
-        if (keys != null && keys.length > 0) {
-            if (keys.length == 1) {
-                redisTemplate.delete(keys[0]);
-            } else {
-                redisTemplate.delete(Arrays.asList(keys));
-            }
-        }
-    }
-
-    // ========================= String 操作 =========================
-
-    /**
-     * 获取字符串类型的值
-     *
-     * @param key 键
-     * @return 值,key 不存在返回 null
-     */
-    public Object get(String key) {
-        return key == null ? null : redisTemplate.opsForValue().get(key);
-    }
-
-    /**
-     * 设置字符串类型的值(永不过期)
-     *
-     * @param key   键
-     * @param value 值
-     * @return 是否成功
-     */
-    public boolean set(String key, Object value) {
-        redisTemplate.opsForValue().set(key, value);
-        return true;
-    }
-
-    /**
-     * 设置字符串类型的值(带过期时间)
-     *
-     * @param key   键
-     * @param value 值
-     * @param time  过期时间(秒),time > 0 才设置过期
-     * @return 是否成功
-     */
-    public boolean set(String key, Object value, long time) {
-        if (time > 0) {
-            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
-        } else {
-            set(key, value);
-        }
-        return true;
-    }
-
-    /**
-     * 递增(常用于计数器)
-     *
-     * @param key   键
-     * @param delta 递增步长(必须 > 0)
-     * @return 递增后的值
-     */
-    public long incr(String key, long delta) {
-        if (delta <= 0) {
-            throw new RuntimeException("递增步长必须大于0");
-        }
-        Long result = redisTemplate.opsForValue().increment(key, delta);
-        return result == null ? 0 : result;
-    }
-
-    /**
-     * 递减(常用于库存扣减)
-     *
-     * @param key   键
-     * @param delta 递减步长(必须 > 0)
-     * @return 递减后的值
-     */
-    public long decr(String key, long delta) {
-        if (delta <= 0) {
-            throw new RuntimeException("递减步长必须大于0");
-        }
-        Long result = redisTemplate.opsForValue().increment(key, -delta);
-        return result == null ? 0 : result;
-    }
-
-    // ========================= Hash 操作 =========================
-
-    /**
-     * 获取 Hash 中指定字段的值
-     *
-     * @param key   键
-     * @param field 字段名
-     * @return 字段值
-     */
-    public Object hget(String key, String field) {
-        return redisTemplate.opsForHash().get(key, field);
-    }
-
-    /**
-     * 获取 Hash 中所有字段和值
-     *
-     * @param key 键
-     * @return 所有字段和值的 Map
-     */
-    public Map<Object, Object> hmget(String key) {
-        return redisTemplate.opsForHash().entries(key);
-    }
-
-    /**
-     * 批量设置 Hash 字段
-     *
-     * @param key 键
-     * @param map 字段-值 Map
-     * @return 是否成功
-     */
-    public boolean hmset(String key, Map<String, Object> map) {
-        redisTemplate.opsForHash().putAll(key, map);
-        return true;
-    }
-
-    /**
-     * 设置 Hash 中单个字段的值
-     *
-     * @param key   键
-     * @param field 字段名
-     * @param value 字段值
-     * @return 是否成功
-     */
-    public boolean hset(String key, String field, Object value) {
-        redisTemplate.opsForHash().put(key, field, value);
-        return true;
-    }
-
-    /**
-     * 删除 Hash 中的字段
-     *
-     * @param key    键
-     * @param fields 要删除的字段(可多个)
-     */
-    public void hdel(String key, Object... fields) {
-        redisTemplate.opsForHash().delete(key, fields);
-    }
-
-    /**
-     * 判断 Hash 中某个字段是否存在
-     *
-     * @param key   键
-     * @param field 字段名
-     * @return true=存在,false=不存在
-     */
-    public boolean hHasKey(String key, String field) {
-        return redisTemplate.opsForHash().hasKey(key, field);
-    }
-
-    // ========================= List 操作 =========================
-
-    /**
-     * 获取 List 中指定范围的元素
-     *
-     * @param key   键
-     * @param start 开始索引(0表示第一个元素)
-     * @param end   结束索引(-1表示最后一个元素)
-     * @return 元素列表
-     */
-    public List<Object> lrange(String key, long start, long end) {
-        return redisTemplate.opsForList().range(key, start, end);
-    }
-
-    /**
-     * 获取 List 的长度
-     *
-     * @param key 键
-     * @return List 长度
-     */
-    public long lsize(String key) {
-        Long size = redisTemplate.opsForList().size(key);
-        return size == null ? 0 : size;
-    }
-
-    /**
-     * 向 List 右端追加元素
-     *
-     * @param key   键
-     * @param value 值
-     * @return 是否成功
-     */
-    public boolean rpush(String key, Object value) {
-        redisTemplate.opsForList().rightPush(key, value);
-        return true;
-    }
-
-    /**
-     * 向 List 右端追加元素(带过期时间)
-     *
-     * @param key   键
-     * @param value 值
-     * @param time  过期时间(秒)
-     * @return 是否成功
-     */
-    public boolean rpush(String key, Object value, long time) {
-        rpush(key, value);
-        if (time > 0) {
-            expire(key, time);
-        }
-        return true;
-    }
-
-    // ========================= Set 操作 =========================
-
-    /**
-     * 获取 Set 中所有元素
-     *
-     * @param key 键
-     * @return Set 元素集合
-     */
-    public Set<Object> smembers(String key) {
-        return redisTemplate.opsForSet().members(key);
-    }
-
-    /**
-     * 向 Set 中添加元素
-     *
-     * @param key    键
-     * @param values 要添加的元素(可多个)
-     * @return 成功添加的元素数量
-     */
-    public long sadd(String key, Object... values) {
-        Long count = redisTemplate.opsForSet().add(key, values);
-        return count == null ? 0 : count;
-    }
-
-    /**
-     * 获取 Set 的元素数量
-     *
-     * @param key 键
-     * @return 元素数量
-     */
-    public long ssize(String key) {
-        Long size = redisTemplate.opsForSet().size(key);
-        return size == null ? 0 : size;
-    }
-
-    /**
-     * 判断元素是否是 Set 的成员
-     *
-     * @param key   键
-     * @param value 元素
-     * @return true=是,false=否
-     */
-    public boolean sisMember(String key, Object value) {
-        return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
-    }
-
-    /**
-     * 从 Set 中删除元素
-     *
-     * @param key    键
-     * @param values 要删除的元素
-     * @return 成功删除的元素数量
-     */
-    public long srem(String key, Object... values) {
-        Long count = redisTemplate.opsForSet().remove(key, values);
-        return count == null ? 0 : count;
-    }
-}

+ 0 - 133
code/backend/src/main/java/com/aijiuyi/admin/common/util/TokenUtil.java

@@ -1,133 +0,0 @@
-package com.aijiuyi.admin.common.util;
-
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.SignatureAlgorithm;
-import org.springframework.util.StringUtils;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * JWT Token 工具类
- * 提供 Token 的生成、解析、验证功能
- * Token 存储用户ID和用户名,默认3年有效期(有效期内可通过 Redis 控制失效)
- */
-public class TokenUtil {
-
-    /** JWT 签名密钥(生产环境请修改为复杂密钥并从配置文件读取) */
-    private static final String SECRET = "aijiuyi_admin_jwt_secret_key_2024_change_in_prod";
-
-    /** Token 默认有效期:3年(毫秒) */
-    private static final long EXPIRATION = 3L * 365 * 24 * 60 * 60 * 1000;
-
-    /** Bearer 前缀 */
-    private static final String TOKEN_PREFIX = "Bearer ";
-
-    /**
-     * 生成 JWT Token(携带 userType)
-     *
-     * @param userId   用户ID
-     * @param username 用户名
-     * @param userType 用户类型(角色)
-     * @return JWT Token 字符串
-     */
-    public static String generateToken(Long userId, String username, Integer userType) {
-        Map<String, Object> claims = new HashMap<>(4);
-        claims.put("userId", userId);
-        claims.put("username", username);
-        claims.put("userType", userType);
-
-        return Jwts.builder()
-                .setClaims(claims)
-                .setIssuedAt(new Date())
-                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
-                .signWith(SignatureAlgorithm.HS256, SECRET)
-                .compact();
-    }
-
-    /**
-     * 生成 JWT Token
-     *
-     * @param userId   用户ID
-     * @param username 用户名
-     * @return JWT Token 字符串
-     */
-    public static String generateToken(Long userId, String username) {
-        Map<String, Object> claims = new HashMap<>(4);
-        claims.put("userId", userId);
-        claims.put("username", username);
-
-        return Jwts.builder()
-                .setClaims(claims)
-                .setIssuedAt(new Date())
-                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
-                .signWith(SignatureAlgorithm.HS256, SECRET)
-                .compact();
-    }
-
-    /**
-     * 解析 JWT Token,获取 Claims
-     * 若 Token 无效或已过期,返回 null
-     *
-     * @param token Token 字符串(可带 Bearer 前缀)
-     * @return Claims,解析失败返回 null
-     */
-    public static Claims parseToken(String token) {
-        if (!StringUtils.hasText(token)) {
-            return null;
-        }
-        // 去掉 Bearer 前缀
-        if (token.startsWith(TOKEN_PREFIX)) {
-            token = token.substring(TOKEN_PREFIX.length());
-        }
-        try {
-            return Jwts.parser()
-                    .setSigningKey(SECRET)
-                    .parseClaimsJws(token)
-                    .getBody();
-        } catch (Exception e) {
-            // Token 无效或已过期
-            return null;
-        }
-    }
-
-    /**
-     * 从 Token 中获取用户ID
-     *
-     * @param token Token 字符串
-     * @return 用户ID,解析失败返回 null
-     */
-    public static Long getUserId(String token) {
-        Claims claims = parseToken(token);
-        if (claims == null) {
-            return null;
-        }
-        return ((Number) claims.get("userId")).longValue();
-    }
-
-    /**
-     * 从 Token 中获取用户名
-     *
-     * @param token Token 字符串
-     * @return 用户名,解析失败返回 null
-     */
-    public static String getUsername(String token) {
-        Claims claims = parseToken(token);
-        if (claims == null) {
-            return null;
-        }
-        return (String) claims.get("username");
-    }
-
-    /**
-     * 验证 Token 是否有效(格式正确且未过期)
-     *
-     * @param token Token 字符串
-     * @return true=有效,false=无效
-     */
-    public static boolean validateToken(String token) {
-        return parseToken(token) != null;
-    }
-}

+ 0 - 144
code/backend/src/main/java/com/aijiuyi/admin/controller/AcupointController.java

@@ -1,144 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.AcupointQueryDTO;
-import com.aijiuyi.admin.controller.dto.YReferenceOptionDTO;
-import com.aijiuyi.admin.entity.Acupoint;
-import com.aijiuyi.admin.service.AcupointService;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 穴位管理 Controller
- * 提供穴位的增删改查、批量删除和批量导入接口
- */
-@RestController
-@RequestMapping("/acupoint")
-public class AcupointController {
-
-    @Autowired
-    private AcupointService acupointService;
-
-    /**
-     * 获取所有启用穴位列表(用于选择框)
-     *
-     * @return 穴位列表(id + name)
-     */
-    @GetMapping("/list")
-    @Log(value = "获取穴位选项列表", module = "穴位管理", operationType = OperationType.QUERY)
-    public Result<List<Acupoint>> list() {
-        List<Acupoint> list = acupointService.list(
-                new LambdaQueryWrapper<Acupoint>()
-                        .eq(Acupoint::getStatus, 1)
-                        .select(Acupoint::getId, Acupoint::getName)
-                        .orderByAsc(Acupoint::getId)
-        );
-        return Result.success(list);
-    }
-
-    /**
-     * 获取Y轴医学定位术语选项(用于新增/编辑穴位时选择)
-     *
-     * @return Y轴定位术语选项
-     */
-    @GetMapping("/y-reference-options")
-    @Log(value = "获取穴位Y轴定位术语", module = "穴位管理", operationType = OperationType.QUERY)
-    public Result<List<YReferenceOptionDTO>> yReferenceOptions() {
-        return Result.success(acupointService.listYReferenceOptions());
-    }
-
-    /**
-     * 分页查询穴位列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @GetMapping("/page")
-    @Log(value = "查询穴位列表", module = "穴位管理", operationType = OperationType.QUERY)
-    public Result<IPage<Acupoint>> page(AcupointQueryDTO queryDTO) {
-        return Result.success(acupointService.pageList(queryDTO));
-    }
-
-    /**
-     * 查询穴位详情
-     *
-     * @param id 穴位ID
-     * @return 穴位详情
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询穴位详情", module = "穴位管理", operationType = OperationType.QUERY)
-    public Result<Acupoint> getById(@PathVariable Long id) {
-        return Result.success(acupointService.getById(id));
-    }
-
-    /**
-     * 新增穴位
-     *
-     * @param acupoint 穴位信息
-     * @return 操作结果
-     */
-    @PostMapping
-    @Log(value = "新增穴位", module = "穴位管理", operationType = OperationType.INSERT)
-    public Result<Void> add(@RequestBody Acupoint acupoint) {
-        acupointService.addAcupoint(acupoint);
-        return Result.success();
-    }
-
-    /**
-     * 修改穴位信息
-     *
-     * @param acupoint 穴位信息
-     * @return 操作结果
-     */
-    @PutMapping
-    @Log(value = "修改穴位信息", module = "穴位管理", operationType = OperationType.UPDATE)
-    public Result<Void> update(@RequestBody Acupoint acupoint) {
-        acupointService.updateAcupoint(acupoint);
-        return Result.success();
-    }
-
-    /**
-     * 删除穴位(逻辑删除)
-     *
-     * @param id 穴位ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{id}")
-    @Log(value = "删除穴位", module = "穴位管理", operationType = OperationType.DELETE)
-    public Result<Void> delete(@PathVariable Long id) {
-        acupointService.deleteAcupoint(id);
-        return Result.success();
-    }
-
-    /**
-     * 批量删除穴位(逻辑删除,需二次确认)
-     *
-     * @param ids 穴位ID列表
-     * @return 操作结果
-     */
-    @DeleteMapping("/batch")
-    @Log(value = "批量删除穴位", module = "穴位管理", operationType = OperationType.DELETE)
-    public Result<Void> batchDelete(@RequestBody List<Long> ids) {
-        acupointService.batchDeleteAcupoint(ids);
-        return Result.success();
-    }
-
-    /**
-     * 批量导入穴位
-     *
-     * @param list 穴位信息列表
-     * @return 操作结果
-     */
-    @PostMapping("/import")
-    @Log(value = "批量导入穴位", module = "穴位管理", operationType = OperationType.INSERT)
-    public Result<Void> batchImport(@RequestBody List<Acupoint> list) {
-        acupointService.batchImport(list);
-        return Result.success();
-    }
-}

+ 0 - 147
code/backend/src/main/java/com/aijiuyi/admin/controller/AdminController.java

@@ -1,147 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.AdminQueryDTO;
-import com.aijiuyi.admin.entity.AppUser;
-import com.aijiuyi.admin.service.AdminService;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 管理员管理 Controller
- * 提供管理员账号的增删改查、状态管理及页面权限配置接口
- */
-@RestController
-@RequestMapping("/admin")
-public class AdminController {
-
-    @Autowired
-    private AdminService adminService;
-
-    /**
-     * 分页查询管理员列表
-     *
-     * @param queryDTO 查询参数(用户名/姓名模糊、角色、状态)
-     * @return 分页管理员列表
-     */
-    @GetMapping("/page")
-    @Log(value = "查询管理员列表", module = "管理员管理", operationType = OperationType.QUERY)
-    public Result<IPage<AppUser>> page(AdminQueryDTO queryDTO) {
-        return Result.success(adminService.pageList(queryDTO));
-    }
-
-    /**
-     * 查询管理员详情
-     *
-     * @param id 管理员ID
-     * @return 管理员信息
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询管理员详情", module = "管理员管理", operationType = OperationType.QUERY)
-    public Result<AppUser> getById(@PathVariable Long id) {
-        return Result.success(adminService.getById(id));
-    }
-
-    /**
-     * 新增管理员
-     *
-     * @param admin 管理员信息(username/password/nickname/adminRole/status 必填)
-     * @return 操作结果
-     */
-    @PostMapping
-    @Log(value = "新增管理员", module = "管理员管理", operationType = OperationType.INSERT)
-    public Result<Void> add(@RequestBody AppUser admin) {
-        adminService.addAdmin(admin);
-        return Result.success();
-    }
-
-    /**
-     * 修改管理员信息
-     *
-     * @param admin 管理员信息(需携带 id;密码为空则不修改)
-     * @return 操作结果
-     */
-    @PutMapping
-    @Log(value = "修改管理员信息", module = "管理员管理", operationType = OperationType.UPDATE)
-    public Result<Void> update(@RequestBody AppUser admin) {
-        adminService.updateAdmin(admin);
-        return Result.success();
-    }
-
-    /**
-     * 删除管理员(逻辑删除,超级管理员不可删除)
-     *
-     * @param id 管理员ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{id}")
-    @Log(value = "删除管理员", module = "管理员管理", operationType = OperationType.DELETE)
-    public Result<Void> delete(@PathVariable Long id) {
-        adminService.deleteAdmin(id);
-        return Result.success();
-    }
-
-    /**
-     * 批量删除管理员
-     *
-     * @param ids 管理员ID列表
-     * @return 操作结果
-     */
-    @DeleteMapping("/batch")
-    @Log(value = "批量删除管理员", module = "管理员管理", operationType = OperationType.DELETE)
-    public Result<Void> batchDelete(@RequestBody List<Long> ids) {
-        adminService.batchDeleteAdmin(ids);
-        return Result.success();
-    }
-
-    /**
-     * 修改管理员状态(禁用/启用)
-     *
-     * @param id     管理员ID
-     * @param status 目标状态:1=启用,0=禁用
-     * @return 操作结果
-     */
-    @PutMapping("/{id}/status")
-    @Log(value = "修改管理员状态", module = "管理员管理", operationType = OperationType.UPDATE)
-    public Result<Void> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
-        adminService.updateStatus(id, status);
-        return Result.success();
-    }
-
-    /**
-     * 查询管理员权限路由列表
-     * 超级管理员返回 null,前端视为全部权限
-     *
-     * @param id 管理员ID
-     * @return 路由路径 JSON 数组字符串
-     */
-    @GetMapping("/{id}/permission")
-    @Log(value = "查询管理员权限", module = "管理员管理", operationType = OperationType.QUERY)
-    public Result<Map<String, Object>> getPermission(@PathVariable Long id) {
-        String routePaths = adminService.getPermissions(id);
-        Map<String, Object> result = new HashMap<>(2);
-        result.put("routePaths", routePaths);
-        return Result.success(result);
-    }
-
-    /**
-     * 更新管理员权限路由列表
-     *
-     * @param id      管理员ID
-     * @param payload 请求体,包含 routePaths(JSON数组字符串)
-     * @return 操作结果
-     */
-    @PutMapping("/{id}/permission")
-    @Log(value = "更新管理员权限", module = "管理员管理", operationType = OperationType.UPDATE)
-    public Result<Void> updatePermission(@PathVariable Long id, @RequestBody Map<String, String> payload) {
-        adminService.updatePermissions(id, payload.get("routePaths"));
-        return Result.success();
-    }
-}

+ 0 - 122
code/backend/src/main/java/com/aijiuyi/admin/controller/AppUserController.java

@@ -1,122 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.AppUserQueryDTO;
-import com.aijiuyi.admin.entity.AppUser;
-import com.aijiuyi.admin.service.AppUserService;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 应用用户管理 Controller(管理员后台)
- * 提供应用用户的增删改查和状态管理接口
- */
-@RestController
-@RequestMapping("/user/app")
-public class AppUserController {
-
-    @Autowired
-    private AppUserService appUserService;
-
-    /**
-     * 获取应用用户选项列表(用于方案创作人选择框)
-     * 返回所有启用状态的用户,前端根据 userType 区分角色
-     *
-     * @param userType 可选过滤:1=普通用户,2=专家,3=管理员;null=全部
-     * @return 用户列表(id + nickname + username + userType)
-     */
-    @GetMapping("/list")
-    @Log(value = "获取用户选项列表", module = "用户管理", operationType = OperationType.QUERY)
-    public Result<List<AppUser>> list(@RequestParam(required = false) Integer userType) {
-        LambdaQueryWrapper<AppUser> wrapper = new LambdaQueryWrapper<AppUser>()
-                .eq(AppUser::getStatus, 1)
-                .eq(userType != null, AppUser::getUserType, userType)
-                .select(AppUser::getId, AppUser::getNickname, AppUser::getUsername, AppUser::getUserType)
-                .orderByAsc(AppUser::getUserType)
-                .orderByAsc(AppUser::getId);
-        return Result.success(appUserService.list(wrapper));
-    }
-
-    /**
-     * 分页查询应用用户列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @GetMapping("/page")
-    @Log(value = "查询应用用户列表", module = "用户管理", operationType = OperationType.QUERY)
-    public Result<IPage<AppUser>> page(AppUserQueryDTO queryDTO) {
-        return Result.success(appUserService.pageList(queryDTO));
-    }
-
-    /**
-     * 查询应用用户详情
-     *
-     * @param id 用户ID
-     * @return 用户详情
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询应用用户详情", module = "用户管理", operationType = OperationType.QUERY)
-    public Result<AppUser> getById(@PathVariable Long id) {
-        return Result.success(appUserService.getById(id));
-    }
-
-    /**
-     * 新增应用用户
-     *
-     * @param appUser 用户信息
-     * @return 操作结果
-     */
-    @PostMapping
-    @Log(value = "新增应用用户", module = "用户管理", operationType = OperationType.INSERT)
-    public Result<Void> add(@RequestBody AppUser appUser) {
-        appUserService.addUser(appUser);
-        return Result.success();
-    }
-
-    /**
-     * 修改应用用户
-     *
-     * @param appUser 用户信息
-     * @return 操作结果
-     */
-    @PutMapping
-    @Log(value = "修改应用用户", module = "用户管理", operationType = OperationType.UPDATE)
-    public Result<Void> update(@RequestBody AppUser appUser) {
-        appUserService.updateUser(appUser);
-        return Result.success();
-    }
-
-    /**
-     * 删除应用用户(逻辑删除)
-     *
-     * @param id 用户ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{id}")
-    @Log(value = "删除应用用户", module = "用户管理", operationType = OperationType.DELETE)
-    public Result<Void> delete(@PathVariable Long id) {
-        appUserService.deleteUser(id);
-        return Result.success();
-    }
-
-    /**
-     * 修改应用用户状态(禁用/启用)
-     *
-     * @param id     用户ID
-     * @param status 目标状态:1=启用,0=禁用
-     * @return 操作结果
-     */
-    @PutMapping("/{id}/status")
-    @Log(value = "修改应用用户状态", module = "用户管理", operationType = OperationType.UPDATE)
-    public Result<Void> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
-        appUserService.updateStatus(id, status);
-        return Result.success();
-    }
-}

+ 0 - 91
code/backend/src/main/java/com/aijiuyi/admin/controller/AuthController.java

@@ -1,91 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.annotation.NoAuth;
-import com.aijiuyi.admin.common.context.RequestContext;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.entity.AppUser;
-import com.aijiuyi.admin.controller.dto.LoginDTO;
-import com.aijiuyi.admin.service.AdminService;
-import com.aijiuyi.admin.service.AppUserService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.validation.Valid;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 认证管理 Controller
- * 提供登录、退出、获取当前用户信息接口
- * 统一使用应用用户表(app_user)进行身份验证
- */
-@RestController
-@RequestMapping("/auth")
-public class AuthController {
-
-    @Autowired
-    private AppUserService appUserService;
-
-    @Autowired
-    private AdminService adminService;
-
-    /**
-     * 用户登录接口
-     * 免鉴权(使用 @NoAuth 注解跳过 Token 验证)
-     * 仅 userType=3(管理员)账号可登录后台
-     *
-     * @param loginDTO 登录参数(用户名、密码)
-     * @return 包含 token 和用户信息的 Result
-     */
-    @PostMapping("/login")
-    @NoAuth
-    @Log(value = "用户登录", module = "认证管理", operationType = OperationType.OTHER)
-    public Result<Map<String, Object>> login(@RequestBody @Valid LoginDTO loginDTO) {
-        return appUserService.login(loginDTO);
-    }
-
-    /**
-     * 用户退出登录
-     * 删除 Redis 中的 Token,使当前 Token 立即失效
-     *
-     * @param request HTTP 请求(用于获取 Authorization 头中的 Token)
-     * @return 操作结果
-     */
-    @PostMapping("/logout")
-    @Log(value = "用户退出", module = "认证管理", operationType = OperationType.OTHER)
-    public Result<Void> logout(HttpServletRequest request) {
-        String token = request.getHeader("Authorization");
-        appUserService.logout(token);
-        return Result.success();
-    }
-
-    /**
-     * 获取当前登录用户信息(含权限路由列表)
-     *
-     * @return 应用用户信息 + permissions 权限路由列表
-     */
-    @GetMapping("/info")
-    @Log(value = "获取当前用户信息", module = "认证管理", operationType = OperationType.QUERY)
-    public Result<Map<String, Object>> getUserInfo() {
-        // 从 ThreadLocal 获取当前用户ID(由 TokenInterceptor 解析 Token 后设置)
-        Long userId = RequestContext.getUserId();
-        AppUser user = appUserService.getById(userId);
-        if (user != null) {
-            user.setPassword(null);
-        }
-        // 查询权限路由列表(超级管理员返回 null,前端视为全部权限)
-        String permissions = adminService.getPermissions(userId);
-        Map<String, Object> data = new HashMap<>(8);
-        data.put("userId", user != null ? user.getId() : null);
-        data.put("username", user != null ? user.getUsername() : null);
-        data.put("nickname", user != null ? user.getNickname() : null);
-        data.put("avatar", user != null ? user.getAvatar() : null);
-        data.put("userType", user != null ? user.getUserType() : null);
-        data.put("adminRole", user != null ? user.getAdminRole() : null);
-        data.put("permissions", permissions);
-        return Result.success(data);
-    }
-}

+ 0 - 186
code/backend/src/main/java/com/aijiuyi/admin/controller/DeviceController.java

@@ -1,186 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.DeviceQueryDTO;
-import com.aijiuyi.admin.controller.dto.DeviceSaveDTO;
-import com.aijiuyi.admin.entity.Device;
-import com.aijiuyi.admin.entity.DeviceUserVO;
-import com.aijiuyi.admin.service.DeviceService;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 设备管理 Controller
- * 提供艾灸椅设备的增删改查、批量导入、以及设备绑定用户管理接口
- */
-@RestController
-@RequestMapping("/device")
-public class DeviceController {
-
-    @Autowired
-    private DeviceService deviceService;
-
-    /**
-     * 分页查询设备列表(含绑定用户数和用户姓名)
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @GetMapping("/page")
-    @Log(value = "查询设备列表", module = "设备管理", operationType = OperationType.QUERY)
-    public Result<IPage<Device>> page(DeviceQueryDTO queryDTO) {
-        return Result.success(deviceService.pageList(queryDTO));
-    }
-
-    /**
-     * 查询设备详情
-     *
-     * @param id 设备ID
-     * @return 设备详情
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询设备详情", module = "设备管理", operationType = OperationType.QUERY)
-    public Result<Device> getById(@PathVariable Long id) {
-        return Result.success(deviceService.getById(id));
-    }
-
-    /**
-     * 新增设备(可同时绑定初始用户)
-     *
-     * @param dto 设备信息
-     * @return 操作结果
-     */
-    @PostMapping
-    @Log(value = "新增设备", module = "设备管理", operationType = OperationType.INSERT)
-    public Result<Void> add(@RequestBody DeviceSaveDTO dto) {
-        deviceService.addDevice(dto);
-        return Result.success();
-    }
-
-    /**
-     * 修改设备信息
-     *
-     * @param dto 设备信息
-     * @return 操作结果
-     */
-    @PutMapping
-    @Log(value = "修改设备信息", module = "设备管理", operationType = OperationType.UPDATE)
-    public Result<Void> update(@RequestBody DeviceSaveDTO dto) {
-        deviceService.updateDevice(dto);
-        return Result.success();
-    }
-
-    /**
-     * 删除设备(逻辑删除)
-     *
-     * @param id 设备ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{id}")
-    @Log(value = "删除设备", module = "设备管理", operationType = OperationType.DELETE)
-    public Result<Void> delete(@PathVariable Long id) {
-        deviceService.deleteDevice(id);
-        return Result.success();
-    }
-
-    /**
-     * 批量删除设备(逻辑删除,需二次确认)
-     *
-     * @param ids 设备ID列表
-     * @return 操作结果
-     */
-    @DeleteMapping("/batch")
-    @Log(value = "批量删除设备", module = "设备管理", operationType = OperationType.DELETE)
-    public Result<Void> batchDelete(@RequestBody List<Long> ids) {
-        deviceService.batchDeleteDevices(ids);
-        return Result.success();
-    }
-
-    /**
-     * 批量导入设备
-     *
-     * @param list 设备信息列表
-     * @return 操作结果
-     */
-    @PostMapping("/import")
-    @Log(value = "批量导入设备", module = "设备管理", operationType = OperationType.INSERT)
-    public Result<Void> batchImport(@RequestBody List<Device> list) {
-        deviceService.batchImport(list);
-        return Result.success();
-    }
-
-    // ======================== 设备用户管理 ========================
-
-    /**
-     * 查询设备绑定的用户列表
-     *
-     * @param deviceId 设备ID
-     * @return 绑定用户列表
-     */
-    @GetMapping("/{deviceId}/users")
-    @Log(value = "查询设备绑定用户列表", module = "设备管理", operationType = OperationType.QUERY)
-    public Result<List<DeviceUserVO>> getDeviceUsers(@PathVariable Long deviceId) {
-        return Result.success(deviceService.getDeviceUsers(deviceId));
-    }
-
-    /**
-     * 为设备添加绑定用户
-     *
-     * @param deviceId  设备ID
-     * @param profileId 用户档案ID(user_profile.id)
-     * @return 操作结果
-     */
-    @PostMapping("/{deviceId}/users/{profileId}")
-    @Log(value = "添加设备绑定用户", module = "设备管理", operationType = OperationType.INSERT)
-    public Result<Void> addDeviceUser(@PathVariable Long deviceId, @PathVariable Long profileId) {
-        deviceService.addDeviceUser(deviceId, profileId);
-        return Result.success();
-    }
-
-    /**
-     * 解绑设备用户
-     *
-     * @param deviceId  设备ID
-     * @param profileId 用户档案ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{deviceId}/users/{profileId}")
-    @Log(value = "解绑设备用户", module = "设备管理", operationType = OperationType.DELETE)
-    public Result<Void> removeDeviceUser(@PathVariable Long deviceId, @PathVariable Long profileId) {
-        deviceService.removeDeviceUser(deviceId, profileId);
-        return Result.success();
-    }
-
-    /**
-     * 设为主用户(同一设备下只有一个主用户)
-     *
-     * @param deviceId  设备ID
-     * @param profileId 用户档案ID
-     * @return 操作结果
-     */
-    @PutMapping("/{deviceId}/users/{profileId}/primary")
-    @Log(value = "设置设备主用户", module = "设备管理", operationType = OperationType.UPDATE)
-    public Result<Void> setDeviceUserPrimary(@PathVariable Long deviceId, @PathVariable Long profileId) {
-        deviceService.setDeviceUserPrimary(deviceId, profileId);
-        return Result.success();
-    }
-
-    /**
-     * 批量解绑设备用户
-     *
-     * @param deviceId 设备ID
-     * @param bindIds  绑定记录ID列表(user_device.id)
-     * @return 操作结果
-     */
-    @DeleteMapping("/{deviceId}/users/batch")
-    @Log(value = "批量解绑设备用户", module = "设备管理", operationType = OperationType.DELETE)
-    public Result<Void> batchRemoveDeviceUsers(@PathVariable Long deviceId, @RequestBody List<Long> bindIds) {
-        deviceService.batchRemoveDeviceUsers(deviceId, bindIds);
-        return Result.success();
-    }
-}

+ 0 - 119
code/backend/src/main/java/com/aijiuyi/admin/controller/MoxibustionTechniqueController.java

@@ -1,119 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.MoxibustionTechniqueQueryDTO;
-import com.aijiuyi.admin.entity.MoxibustionTechnique;
-import com.aijiuyi.admin.service.MoxibustionTechniqueService;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 艾灸手法管理 Controller
- * 提供手法的增删改查、批量删除接口
- */
-@RestController
-@RequestMapping("/moxibustion-technique")
-public class MoxibustionTechniqueController {
-
-    @Autowired
-    private MoxibustionTechniqueService techniqueService;
-
-    /**
-     * 获取所有启用艾灸手法列表(用于选择框)
-     *
-     * @return 手法列表(id + name)
-     */
-    @GetMapping("/list")
-    @Log(value = "获取手法选项列表", module = "艾灸手法管理", operationType = OperationType.QUERY)
-    public Result<List<MoxibustionTechnique>> list() {
-        List<MoxibustionTechnique> list = techniqueService.list(
-                new LambdaQueryWrapper<MoxibustionTechnique>()
-                        .eq(MoxibustionTechnique::getStatus, 1)
-                        .select(MoxibustionTechnique::getId, MoxibustionTechnique::getName)
-                        .orderByAsc(MoxibustionTechnique::getId)
-        );
-        return Result.success(list);
-    }
-
-    /**
-     * 分页查询艾灸手法列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @GetMapping("/page")
-    @Log(value = "查询艾灸手法列表", module = "艾灸手法管理", operationType = OperationType.QUERY)
-    public Result<IPage<MoxibustionTechnique>> page(MoxibustionTechniqueQueryDTO queryDTO) {
-        return Result.success(techniqueService.pageList(queryDTO));
-    }
-
-    /**
-     * 查询艾灸手法详情
-     *
-     * @param id 手法ID
-     * @return 手法详情
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询艾灸手法详情", module = "艾灸手法管理", operationType = OperationType.QUERY)
-    public Result<MoxibustionTechnique> getById(@PathVariable Long id) {
-        return Result.success(techniqueService.getById(id));
-    }
-
-    /**
-     * 新增艾灸手法
-     *
-     * @param technique 手法信息
-     * @return 操作结果
-     */
-    @PostMapping
-    @Log(value = "新增艾灸手法", module = "艾灸手法管理", operationType = OperationType.INSERT)
-    public Result<Void> add(@RequestBody MoxibustionTechnique technique) {
-        techniqueService.addTechnique(technique);
-        return Result.success();
-    }
-
-    /**
-     * 修改艾灸手法信息
-     *
-     * @param technique 手法信息
-     * @return 操作结果
-     */
-    @PutMapping
-    @Log(value = "修改艾灸手法", module = "艾灸手法管理", operationType = OperationType.UPDATE)
-    public Result<Void> update(@RequestBody MoxibustionTechnique technique) {
-        techniqueService.updateTechnique(technique);
-        return Result.success();
-    }
-
-    /**
-     * 删除艾灸手法(逻辑删除)
-     *
-     * @param id 手法ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{id}")
-    @Log(value = "删除艾灸手法", module = "艾灸手法管理", operationType = OperationType.DELETE)
-    public Result<Void> delete(@PathVariable Long id) {
-        techniqueService.deleteTechnique(id);
-        return Result.success();
-    }
-
-    /**
-     * 批量删除艾灸手法(逻辑删除)
-     *
-     * @param ids 手法ID列表
-     * @return 操作结果
-     */
-    @DeleteMapping("/batch")
-    @Log(value = "批量删除艾灸手法", module = "艾灸手法管理", operationType = OperationType.DELETE)
-    public Result<Void> batchDelete(@RequestBody List<Long> ids) {
-        techniqueService.batchDeleteTechnique(ids);
-        return Result.success();
-    }
-}

+ 0 - 154
code/backend/src/main/java/com/aijiuyi/admin/controller/PlanController.java

@@ -1,154 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.PlanQueryDTO;
-import com.aijiuyi.admin.controller.dto.PlanSaveDTO;
-import com.aijiuyi.admin.entity.Plan;
-import com.aijiuyi.admin.service.PlanService;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 方案管理 Controller
- * 提供方案的新增、编辑、发布、下架、删除等接口
- */
-@RestController
-@RequestMapping("/plan")
-public class PlanController {
-
-    @Autowired
-    private PlanService planService;
-
-    /**
-     * 分页查询方案列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @GetMapping("/page")
-    @Log(value = "查询方案列表", module = "方案管理", operationType = OperationType.QUERY)
-    public Result<IPage<Plan>> page(PlanQueryDTO queryDTO) {
-        return Result.success(planService.pageList(queryDTO));
-    }
-
-    /**
-     * 查询方案详情(含步骤列表)
-     *
-     * @param id 方案ID
-     * @return 方案详情
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询方案详情", module = "方案管理", operationType = OperationType.QUERY)
-    public Result<PlanSaveDTO> getById(@PathVariable Long id) {
-        return Result.success(planService.getDetail(id));
-    }
-
-    /**
-     * 新增方案(保存为草稿)
-     *
-     * @param dto 方案信息(含步骤)
-     * @return 操作结果
-     */
-    @PostMapping
-    @Log(value = "新增方案", module = "方案管理", operationType = OperationType.INSERT)
-    public Result<Void> add(@RequestBody PlanSaveDTO dto) {
-        planService.addPlan(dto);
-        return Result.success();
-    }
-
-    /**
-     * 修改方案信息(含步骤)
-     *
-     * @param dto 方案信息(含步骤)
-     * @return 操作结果
-     */
-    @PutMapping
-    @Log(value = "修改方案信息", module = "方案管理", operationType = OperationType.UPDATE)
-    public Result<Void> update(@RequestBody PlanSaveDTO dto) {
-        planService.updatePlan(dto);
-        return Result.success();
-    }
-
-    /**
-     * 删除方案(逻辑删除,仅草稿可删)
-     *
-     * @param id 方案ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{id}")
-    @Log(value = "删除方案", module = "方案管理", operationType = OperationType.DELETE)
-    public Result<Void> delete(@PathVariable Long id) {
-        planService.deletePlan(id);
-        return Result.success();
-    }
-
-    /**
-     * 批量删除方案(仅草稿可删,需二次确认)
-     *
-     * @param ids 方案ID列表
-     * @return 操作结果
-     */
-    @DeleteMapping("/batch")
-    @Log(value = "批量删除方案", module = "方案管理", operationType = OperationType.DELETE)
-    public Result<Void> batchDelete(@RequestBody List<Long> ids) {
-        planService.batchDeletePlan(ids);
-        return Result.success();
-    }
-
-    /**
-     * 发布方案(草稿 → 已发布)
-     *
-     * @param id 方案ID
-     * @return 操作结果
-     */
-    @PutMapping("/{id}/publish")
-    @Log(value = "发布方案", module = "方案管理", operationType = OperationType.UPDATE)
-    public Result<Void> publish(@PathVariable Long id) {
-        planService.publishPlan(id);
-        return Result.success();
-    }
-
-    /**
-     * 批量发布方案
-     *
-     * @param ids 方案ID列表
-     * @return 操作结果
-     */
-    @PutMapping("/batch/publish")
-    @Log(value = "批量发布方案", module = "方案管理", operationType = OperationType.UPDATE)
-    public Result<Void> batchPublish(@RequestBody List<Long> ids) {
-        planService.batchPublishPlan(ids);
-        return Result.success();
-    }
-
-    /**
-     * 下架方案(已发布 → 已下架)
-     *
-     * @param id 方案ID
-     * @return 操作结果
-     */
-    @PutMapping("/{id}/offshelf")
-    @Log(value = "下架方案", module = "方案管理", operationType = OperationType.UPDATE)
-    public Result<Void> offshelf(@PathVariable Long id) {
-        planService.offshelfPlan(id);
-        return Result.success();
-    }
-
-    /**
-     * 批量下架方案
-     *
-     * @param ids 方案ID列表
-     * @return 操作结果
-     */
-    @PutMapping("/batch/offshelf")
-    @Log(value = "批量下架方案", module = "方案管理", operationType = OperationType.UPDATE)
-    public Result<Void> batchOffshelf(@RequestBody List<Long> ids) {
-        planService.batchOffshelfPlan(ids);
-        return Result.success();
-    }
-}

+ 0 - 36
code/backend/src/main/java/com/aijiuyi/admin/controller/SimulationController.java

@@ -1,36 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.SimulationResponse;
-import com.aijiuyi.admin.service.SimulationService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-/**
- * 方案模拟测试 Controller
- * 模拟小程序选择方案后,系统对穴位坐标的计算结果
- */
-@RestController
-@RequestMapping("/simulation")
-public class SimulationController {
-
-    @Autowired
-    private SimulationService simulationService;
-
-    /**
-     * 组装方案模拟推送数据
-     *
-     * @param profileId 子用户ID
-     * @param planId    方案ID
-     * @return 推送数据(含体型、方案步骤、穴位坐标偏移)
-     */
-    @GetMapping("/push-data/{profileId}/{planId}")
-    @Log(value = "方案模拟测试", module = "模拟测试", operationType = OperationType.QUERY)
-    public Result<SimulationResponse> getPushData(
-            @PathVariable Long profileId,
-            @PathVariable Long planId) {
-        return Result.success(simulationService.assemblePushData(profileId, planId));
-    }
-}

+ 0 - 63
code/backend/src/main/java/com/aijiuyi/admin/controller/SysLogController.java

@@ -1,63 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.entity.SysLog;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.common.service.SysLogService;
-import com.aijiuyi.admin.controller.dto.SysLogQueryDTO;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * 日志管理 Controller
- * 提供操作日志的分页查询、全量导出和管理员选项接口
- */
-@RestController
-@RequestMapping("/log")
-public class SysLogController {
-
-    @Autowired
-    private SysLogService sysLogService;
-
-    /**
-     * 分页查询操作日志列表
-     *
-     * @param queryDTO 查询条件(操作人、操作类型、操作模块、时间范围、分页参数)
-     * @return 分页日志列表
-     */
-    @GetMapping("/page")
-    @Log(value = "查询操作日志列表", module = "日志管理", operationType = OperationType.QUERY)
-    public Result<IPage<SysLog>> page(SysLogQueryDTO queryDTO) {
-        return Result.success(sysLogService.pageList(queryDTO));
-    }
-
-    /**
-     * 导出全部操作日志(返回全量数据供前端 Excel 导出使用)
-     *
-     * @param queryDTO 查询条件(不含分页参数)
-     * @return 日志列表
-     */
-    @GetMapping("/export")
-    @Log(value = "导出操作日志", module = "日志管理", operationType = OperationType.OTHER)
-    public Result<List<SysLog>> export(SysLogQueryDTO queryDTO) {
-        return Result.success(sysLogService.listAll(queryDTO));
-    }
-
-    /**
-     * 获取系统管理员选项列表(用于前端操作人下拉框)
-     *
-     * @return 管理员选项列表(含 id、label、username)
-     */
-    @GetMapping("/user/options")
-    @Log(value = "获取管理员选项", module = "日志管理", operationType = OperationType.QUERY)
-    public Result<List<Map<String, Object>>> getSysUserOptions() {
-        return Result.success(sysLogService.getSysUserOptions());
-    }
-}

+ 0 - 48
code/backend/src/main/java/com/aijiuyi/admin/controller/UserAcupointController.java

@@ -1,48 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.entity.UserAcupoint;
-import com.aijiuyi.admin.service.UserAcupointService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 用户穴位坐标 Controller(管理员后台)
- * 提供手动触发生成、查询穴位坐标接口
- */
-@RestController
-@RequestMapping("/user/acupoint")
-public class UserAcupointController {
-
-    @Autowired
-    private UserAcupointService userAcupointService;
-
-    /**
-     * 手动为指定子用户生成(或重新生成)穴位坐标表
-     *
-     * @param profileId 子用户ID(user_profile.id)
-     * @return 操作结果
-     */
-    @PostMapping("/{profileId}/generate")
-    @Log(value = "生成用户穴位坐标", module = "用户穴位管理", operationType = OperationType.INSERT)
-    public Result<Void> generate(@PathVariable Long profileId) {
-        userAcupointService.generateForUser(profileId);
-        return Result.success();
-    }
-
-    /**
-     * 查询指定子用户的穴位坐标列表
-     *
-     * @param profileId 子用户ID
-     * @return 穴位坐标列表
-     */
-    @GetMapping("/{profileId}/list")
-    @Log(value = "查询用户穴位坐标列表", module = "用户穴位管理", operationType = OperationType.QUERY)
-    public Result<List<UserAcupoint>> list(@PathVariable Long profileId) {
-        return Result.success(userAcupointService.listByUser(profileId));
-    }
-}

+ 0 - 115
code/backend/src/main/java/com/aijiuyi/admin/controller/UserDeviceController.java

@@ -1,115 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.UserDeviceQueryDTO;
-import com.aijiuyi.admin.entity.UserDevice;
-import com.aijiuyi.admin.service.UserDeviceService;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 用户设备管理 Controller(管理员后台)
- * 提供设备绑定记录的查询、新增、解绑、设为主设备、批量解绑接口
- */
-@RestController
-@RequestMapping("/user/device")
-public class UserDeviceController {
-
-    @Autowired
-    private UserDeviceService userDeviceService;
-
-    /**
-     * 分页查询设备列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @GetMapping("/page")
-    @Log(value = "查询设备列表", module = "设备管理", operationType = OperationType.QUERY)
-    public Result<IPage<UserDevice>> page(UserDeviceQueryDTO queryDTO) {
-        return Result.success(userDeviceService.pageList(queryDTO));
-    }
-
-    /**
-     * 查询设备详情
-     *
-     * @param id 设备记录ID
-     * @return 设备详情
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询设备详情", module = "设备管理", operationType = OperationType.QUERY)
-    public Result<UserDevice> getById(@PathVariable Long id) {
-        return Result.success(userDeviceService.getById(id));
-    }
-
-    /**
-     * 绑定设备(新增绑定记录)
-     *
-     * @param device 设备信息(userId为子用户ID,deviceCode为设备编号)
-     * @return 操作结果
-     */
-    @PostMapping
-    @Log(value = "绑定设备", module = "设备管理", operationType = OperationType.INSERT)
-    public Result<Void> bind(@RequestBody UserDevice device) {
-        userDeviceService.bindDevice(device);
-        return Result.success();
-    }
-
-    /**
-     * 解绑设备(设置状态为已解绑)
-     *
-     * @param id 设备记录ID
-     * @return 操作结果
-     */
-    @PutMapping("/{id}/unbind")
-    @Log(value = "解绑设备", module = "设备管理", operationType = OperationType.UPDATE)
-    public Result<Void> unbind(@PathVariable Long id) {
-        userDeviceService.unbindDevice(id);
-        return Result.success();
-    }
-
-    /**
-     * 设置主设备(同一用户下只能有一个主设备)
-     *
-     * @param id     设备记录ID
-     * @param userId 所属子用户ID
-     * @return 操作结果
-     */
-    @PutMapping("/{id}/primary")
-    @Log(value = "设置主设备", module = "设备管理", operationType = OperationType.UPDATE)
-    public Result<Void> setPrimary(@PathVariable Long id, @RequestParam Long userId) {
-        userDeviceService.setPrimaryDevice(id, userId);
-        return Result.success();
-    }
-
-    /**
-     * 批量解绑设备
-     *
-     * @param ids 设备记录ID列表
-     * @return 操作结果
-     */
-    @PutMapping("/batch-unbind")
-    @Log(value = "批量解绑设备", module = "设备管理", operationType = OperationType.UPDATE)
-    public Result<Void> batchUnbind(@RequestBody List<Long> ids) {
-        userDeviceService.batchUnbindDevices(ids);
-        return Result.success();
-    }
-
-    /**
-     * 删除设备记录
-     *
-     * @param id 设备记录ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{id}")
-    @Log(value = "删除设备记录", module = "设备管理", operationType = OperationType.DELETE)
-    public Result<Void> delete(@PathVariable Long id) {
-        userDeviceService.deleteDevice(id);
-        return Result.success();
-    }
-}

+ 0 - 51
code/backend/src/main/java/com/aijiuyi/admin/controller/UserPlanController.java

@@ -1,51 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.entity.UserPlan;
-import com.aijiuyi.admin.service.UserPlanService;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-/**
- * 用户方案 Controller
- * 提供查询指定子用户的方案列表及详情接口
- */
-@RestController
-@RequestMapping("/user-plan")
-public class UserPlanController {
-
-    @Autowired
-    private UserPlanService userPlanService;
-
-    /**
-     * 分页查询指定子用户的方案列表
-     *
-     * @param userId   子用户ID
-     * @param pageNum  当前页码(默认1)
-     * @param pageSize 每页条数(默认10)
-     * @return 分页结果
-     */
-    @GetMapping("/page")
-    @Log(value = "查询用户方案列表", module = "用户方案管理", operationType = OperationType.QUERY)
-    public Result<IPage<UserPlan>> page(
-            @RequestParam Long userId,
-            @RequestParam(defaultValue = "1") Integer pageNum,
-            @RequestParam(defaultValue = "10") Integer pageSize) {
-        return Result.success(userPlanService.pageByUserId(userId, pageNum, pageSize));
-    }
-
-    /**
-     * 查询用户方案详情
-     *
-     * @param id 用户方案ID
-     * @return 用户方案详情
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询用户方案详情", module = "用户方案管理", operationType = OperationType.QUERY)
-    public Result<UserPlan> getById(@PathVariable Long id) {
-        return Result.success(userPlanService.getDetail(id));
-    }
-}

+ 0 - 140
code/backend/src/main/java/com/aijiuyi/admin/controller/UserProfileController.java

@@ -1,140 +0,0 @@
-package com.aijiuyi.admin.controller;
-
-import com.aijiuyi.admin.common.annotation.Log;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.enums.OperationType;
-import com.aijiuyi.admin.controller.dto.UserProfileDTO;
-import com.aijiuyi.admin.controller.dto.UserProfileQueryDTO;
-import com.aijiuyi.admin.entity.UserProfile;
-import com.aijiuyi.admin.service.UserProfileService;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import javax.validation.Valid;
-import java.util.List;
-
-/**
- * 用户管理 Controller(管理员后台)
- * 提供用户档案的增删改查、批量删除、批量导入接口
- */
-@RestController
-@RequestMapping("/user/profile")
-public class UserProfileController {
-
-    @Autowired
-    private UserProfileService userProfileService;
-
-    /**
-     * 分页查询用户列表(联表获取设备编号)
-     *
-     * @param queryDTO 查询条件(关键字/设备编号/性别/注册时间范围/穴位表状态)
-     * @return 分页结果
-     */
-    @GetMapping("/page")
-    @Log(value = "查询用户列表", module = "用户管理", operationType = OperationType.QUERY)
-    public Result<IPage<UserProfile>> page(UserProfileQueryDTO queryDTO) {
-        return Result.success(userProfileService.pageList(queryDTO));
-    }
-
-
-
-    /**
-     * 查询用户详情
-     *
-     * @param id 用户ID
-     * @return 用户详情
-     */
-    @GetMapping("/{id}")
-    @Log(value = "查询用户详情", module = "用户管理", operationType = OperationType.QUERY)
-    public Result<UserProfile> getById(@PathVariable Long id) {
-        return Result.success(userProfileService.getById(id));
-    }
-
-    /**
-     * 新增用户
-     *
-     * @param dto 用户信息
-     * @return 操作结果
-     */
-    @PostMapping
-    @Log(value = "新增用户", module = "用户管理", operationType = OperationType.INSERT)
-    public Result<Void> add(@RequestBody @Valid UserProfileDTO dto) {
-        userProfileService.addProfile(dto);
-        return Result.success();
-    }
-
-    /**
-     * 修改用户
-     *
-     * @param dto 用户信息
-     * @return 操作结果
-     */
-    @PutMapping
-    @Log(value = "修改用户", module = "用户管理", operationType = OperationType.UPDATE)
-    public Result<Void> update(@RequestBody @Valid UserProfileDTO dto) {
-        userProfileService.updateProfile(dto);
-        return Result.success();
-    }
-
-    /**
-     * 删除用户
-     *
-     * @param id 用户ID
-     * @return 操作结果
-     */
-    @DeleteMapping("/{id}")
-    @Log(value = "删除用户", module = "用户管理", operationType = OperationType.DELETE)
-    public Result<Void> delete(@PathVariable Long id) {
-        userProfileService.deleteProfile(id);
-        return Result.success();
-    }
-
-    /**
-     * 批量删除用户
-     *
-     * @param ids 用户ID列表
-     * @return 操作结果
-     */
-    @DeleteMapping("/batch")
-    @Log(value = "批量删除用户", module = "用户管理", operationType = OperationType.DELETE)
-    public Result<Void> batchDelete(@RequestBody List<Long> ids) {
-        userProfileService.batchDelete(ids);
-        return Result.success();
-    }
-
-    /**
-     * 批量导入用户
-     *
-     * @param list 用户信息列表
-     * @return 操作结果
-     */
-    @PostMapping("/import")
-    @Log(value = "批量导入用户", module = "用户管理", operationType = OperationType.INSERT)
-    public Result<Void> batchImport(@RequestBody List<UserProfileDTO> list) {
-        userProfileService.batchImport(list);
-        return Result.success();
-    }
-
-    /**
-     * 查询用户档案选项列表(用于设备绑定用户下拉选择)
-     *
-     * @param keyword 搜索关键字(姓名或手机号,选填)
-     * @return 用户列表(id/name/phone/gender)
-     */
-    @GetMapping("/options")
-    @Log(value = "查询用户选项列表", module = "用户管理", operationType = OperationType.QUERY)
-    public Result<List<UserProfile>> options(@RequestParam(required = false) String keyword) {
-        return Result.success(userProfileService.listOptions(keyword));
-    }
-
-    /**
-     * 获取所有用户列表(用于下拉选择)
-     */
-    @GetMapping("/list-all")
-    @Log(value = "获取所有用户列表", module = "用户管理", operationType = OperationType.QUERY)
-    public Result<List<UserProfile>> listAll() {
-        return Result.success(userProfileService.list());
-    }
-}
-

+ 0 - 28
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/AcupointQueryDTO.java

@@ -1,28 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 穴位查询条件 DTO
- */
-@Data
-public class AcupointQueryDTO {
-
-    /** 穴位名称(模糊搜索) */
-    private String name;
-
-    /** 所属经络 */
-    private String meridian;
-
-    /** 定位方式:当前固定为相对定位,保留字段兼容旧查询参数 */
-    private Integer locationType;
-
-    /** 状态:1=活跃,0=待完善 */
-    private Integer status;
-
-    /** 当前页码,默认第1页 */
-    private Integer pageNum = 1;
-
-    /** 每页条数,默认10条 */
-    private Integer pageSize = 10;
-}

+ 0 - 28
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/AdminQueryDTO.java

@@ -1,28 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 管理员分页查询参数 DTO
- */
-@Data
-public class AdminQueryDTO {
-
-    /** 用户名(模糊查询) */
-    private String username;
-
-    /** 姓名/昵称(模糊查询) */
-    private String nickname;
-
-    /** 管理员角色:1=超级管理员,2=运营人员,3=只读用户 */
-    private Integer adminRole;
-
-    /** 状态:1=启用,0=禁用 */
-    private Integer status;
-
-    /** 当前页码,默认第1页 */
-    private Integer pageNum = 1;
-
-    /** 每页条数,默认10条 */
-    private Integer pageSize = 10;
-}

+ 0 - 28
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/AppUserQueryDTO.java

@@ -1,28 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 应用用户查询请求 DTO
- */
-@Data
-public class AppUserQueryDTO {
-
-    /** 手机号(模糊搜索) */
-    private String phone;
-
-    /** 昵称(模糊搜索) */
-    private String nickname;
-
-    /** 用户类型:1=普通用户,2=专家,3=管理员 */
-    private Integer userType;
-
-    /** 状态:1=正常,0=禁用 */
-    private Integer status;
-
-    /** 当前页码(从1开始) */
-    private Integer pageNum = 1;
-
-    /** 每页条数 */
-    private Integer pageSize = 10;
-}

+ 0 - 31
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/DeviceQueryDTO.java

@@ -1,31 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 设备管理列表查询请求 DTO
- */
-@Data
-public class DeviceQueryDTO {
-
-    /** 设备编号(模糊搜索) */
-    private String deviceCode;
-
-    /** 设备名称(模糊搜索) */
-    private String deviceName;
-
-    /** 绑定用户姓名(模糊搜索,匹配该设备下任意绑定用户) */
-    private String boundUserName;
-
-    /** 在线状态:1=在线,0=离线,null=全部 */
-    private Integer onlineStatus;
-
-    /** 固件版本(精确匹配,null=全部) */
-    private String firmwareVersion;
-
-    /** 当前页码(从1开始) */
-    private Integer pageNum = 1;
-
-    /** 每页条数 */
-    private Integer pageSize = 10;
-}

+ 0 - 39
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/DeviceSaveDTO.java

@@ -1,39 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-import java.util.List;
-
-/**
- * 设备新增/编辑请求 DTO
- */
-@Data
-public class DeviceSaveDTO {
-
-    /** 主键ID(新增时为 null,编辑时必填) */
-    private Long id;
-
-    /** 设备编号(唯一,新增必填,编辑时不可修改) */
-    private String deviceCode;
-
-    /** 设备名称(必填,最长100字符) */
-    private String deviceName;
-
-    /** 设备型号(必填,AJY-2026 / AJY-2025) */
-    private String deviceModel;
-
-    /** 设备序列号(唯一,必填,最长50字符) */
-    private String serialNo;
-
-    /** 固件版本(必填,如:v1.2.0) */
-    private String firmwareVersion;
-
-    /** 备注(选填,最长200字符) */
-    private String remark;
-
-    /**
-     * 绑定用户ID列表(user_profile.id,选填)
-     * 新增:绑定初始用户;编辑:与当前绑定用户做差集合并(null 表示不变更绑定关系,空列表表示清空所有绑定)
-     */
-    private List<Long> userIds;
-}

+ 0 - 20
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/LoginDTO.java

@@ -1,20 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-import javax.validation.constraints.NotBlank;
-
-/**
- * 登录请求参数 DTO
- */
-@Data
-public class LoginDTO {
-
-    /** 用户名(不能为空) */
-    @NotBlank(message = "用户名不能为空")
-    private String username;
-
-    /** 密码(不能为空,前端传明文,后端做 MD5 加密后比对) */
-    @NotBlank(message = "密码不能为空")
-    private String password;
-}

+ 0 - 25
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/MoxibustionTechniqueQueryDTO.java

@@ -1,25 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 艾灸手法查询条件 DTO
- */
-@Data
-public class MoxibustionTechniqueQueryDTO {
-
-    /** 手法名称(模糊搜索) */
-    private String name;
-
-    /** 运动模式(精确匹配) */
-    private String motionMode;
-
-    /** 状态:1=启用,0=禁用 */
-    private Integer status;
-
-    /** 当前页码,默认第1页 */
-    private Integer pageNum = 1;
-
-    /** 每页条数,默认10条 */
-    private Integer pageSize = 10;
-}

+ 0 - 37
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/PlanQueryDTO.java

@@ -1,37 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 方案列表查询条件 DTO
- */
-@Data
-public class PlanQueryDTO {
-
-    /** 方案名称(模糊搜索) */
-    private String name;
-
-    /** 模式类型:1=一键艾灸,2=专业模式,3=自定义模式,4=延年圣手 */
-    private Integer modeType;
-
-    /** 功效类型:驱寒/祛湿/祛风/化瘀/活血/化痰/养颜/扶阳 */
-    private String effectType;
-
-    /** 创作人名称(模糊搜索) */
-    private String authorName;
-
-    /** 状态:0=草稿,1=已发布,2=已下架 */
-    private Integer status;
-
-    /** 创建时间开始 */
-    private String createTimeStart;
-
-    /** 创建时间结束 */
-    private String createTimeEnd;
-
-    /** 当前页码,默认第1页 */
-    private Integer pageNum = 1;
-
-    /** 每页条数,默认10条 */
-    private Integer pageSize = 10;
-}

+ 0 - 58
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/PlanSaveDTO.java

@@ -1,58 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import com.aijiuyi.admin.entity.PlanStep;
-import lombok.Data;
-
-import java.util.List;
-
-/**
- * 方案新增/编辑 DTO(含步骤列表)
- */
-@Data
-public class PlanSaveDTO {
-
-    /** 方案ID(编辑时传递,新增时为空) */
-    private Long id;
-
-    /** 方案编码(新增时由系统生成,编辑时不可修改) */
-    private String planCode;
-
-    /** 方案名称(最长100字符) */
-    private String name;
-
-    /** 模式类型:1=一键艾灸,2=专业模式,3=自定义模式,4=延年圣手 */
-    private Integer modeType;
-
-    /** 功效类型:驱寒/祛湿/祛风/化瘀/活血/化痰/养颜/扶阳 */
-    private String effectType;
-
-    /** 适用症状(逗号分隔) */
-    private String symptoms;
-
-    /** 适用人群:0=不限,1=仅男,2=仅女 */
-    private Integer applicableGender;
-
-    /** 适用年龄最小值(岁) */
-    private Integer ageMin;
-
-    /** 适用年龄最大值(岁) */
-    private Integer ageMax;
-
-    /** 方案描述(最长200字符) */
-    private String description;
-
-    /** 创作人类型:1=管理员,2=普通用户,3=专家 */
-    private Integer authorType;
-
-    /** 创作人ID */
-    private Long authorId;
-
-    /** 创作人名称 */
-    private String authorName;
-
-    /** 状态:0=草稿,1=已发布,2=已下架(新增时默认草稿) */
-    private Integer status;
-
-    /** 治疗步骤列表 */
-    private List<PlanStep> steps;
-}

+ 0 - 63
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/SimulationResponse.java

@@ -1,63 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-import java.math.BigDecimal;
-import java.util.List;
-
-/**
- * 方案模拟测试响应 DTO
- * 组装用户体型数据 + 方案步骤 + 穴位坐标,用于前端可视化展示
- */
-@Data
-public class SimulationResponse {
-
-    private Long userId;
-    private String userName;
-    private Integer modeType;
-    private BodyData bodyData;
-    private SchemeData scheme;
-
-    @Data
-    public static class BodyData {
-        private Integer gender;
-        private Integer age;
-        private BigDecimal shoulderWidth;
-        private BigDecimal bodyHeight;
-        private BigDecimal spineLength;
-        private BigDecimal height;
-        private BigDecimal weight;
-        private FingerWidth fingerWidth;
-    }
-
-    @Data
-    public static class FingerWidth {
-        private BigDecimal cun1;
-        private BigDecimal cun1_5;
-        private BigDecimal cun3;
-    }
-
-    @Data
-    public static class SchemeData {
-        private Long planId;
-        private String planCode;
-        private String planName;
-        private String effectType;
-        private List<StepData> steps;
-    }
-
-    @Data
-    public static class StepData {
-        private Integer stepOrder;
-        private Long acupointId;
-        private String acupointName;
-        private String acupointSide;
-        private BigDecimal offsetX;
-        private BigDecimal offsetY;
-        private BigDecimal temperature;
-        private Integer duration;
-        private Long techniqueId;
-        private String techniqueName;
-        private String confidence;
-    }
-}

+ 0 - 32
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/SysLogQueryDTO.java

@@ -1,32 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 日志查询请求参数 DTO
- * 用于 /log/page 和 /log/export 接口的查询条件封装
- */
-@Data
-public class SysLogQueryDTO {
-
-    /** 操作人ID(对应 sys_log.user_id) */
-    private Long userId;
-
-    /** 操作类型(查询/新增/更新/删除/其他) */
-    private String operationType;
-
-    /** 操作模块(用户管理/穴位管理/方案管理/设备管理等) */
-    private String operationModule;
-
-    /** 开始时间(格式:yyyy-MM-dd HH:mm:ss) */
-    private String startTime;
-
-    /** 结束时间(格式:yyyy-MM-dd HH:mm:ss) */
-    private String endTime;
-
-    /** 当前页(默认第1页) */
-    private Integer pageNum = 1;
-
-    /** 每页条数(默认10条) */
-    private Integer pageSize = 10;
-}

+ 0 - 28
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/UserDeviceQueryDTO.java

@@ -1,28 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 用户设备查询请求 DTO
- */
-@Data
-public class UserDeviceQueryDTO {
-
-    /** 所属应用用户ID */
-    private Long userId;
-
-    /** 设备编码(模糊搜索) */
-    private String deviceCode;
-
-    /** 设备名称(模糊搜索) */
-    private String deviceName;
-
-    /** 绑定状态:1=已绑定,0=已解绑 */
-    private Integer status;
-
-    /** 当前页码(从1开始) */
-    private Integer pageNum = 1;
-
-    /** 每页条数 */
-    private Integer pageSize = 10;
-}

+ 0 - 19
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/UserPlanQueryDTO.java

@@ -1,19 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 用户方案查询请求 DTO
- */
-@Data
-public class UserPlanQueryDTO {
-
-    /** 用户ID */
-    private Long userId;
-
-    /** 当前页码(从1开始) */
-    private Integer pageNum = 1;
-
-    /** 每页条数 */
-    private Integer pageSize = 10;
-}

+ 0 - 68
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/UserProfileDTO.java

@@ -1,68 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
-import java.math.BigDecimal;
-
-/**
- * 子用户新增/修改请求 DTO
- */
-@Data
-public class UserProfileDTO {
-
-    /** 子用户ID(修改时必填,新增时不填) */
-    private Long id;
-
-    /** 所属应用用户ID(新增时必填) */
-    private Long userId;
-
-    /** 姓名(必填) */
-    @NotBlank(message = "姓名不能为空")
-    private String name;
-
-    /** 性别:1=男,2=女 */
-    @NotNull(message = "性别不能为空")
-    private Integer gender;
-
-    /** 年龄(必填,1-120) */
-    @NotNull(message = "年龄不能为空")
-    private Integer age;
-
-    /** 手机号(必填,11位格式) */
-    @NotBlank(message = "手机号不能为空")
-    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
-    private String phone;
-
-    /** 肩宽(cm,必填) */
-    @NotNull(message = "肩宽不能为空")
-    private BigDecimal shoulderWidth;
-
-    /** 身长(cm,必填) */
-    @NotNull(message = "身长不能为空")
-    private BigDecimal bodyHeight;
-
-    /** 脊柱长度C7-S4(cm,必填,30-60),用于按医学定位术语计算Y轴坐标 */
-    @NotNull(message = "脊柱长度不能为空")
-    private BigDecimal spineLength;
-
-    /** 身高(cm,选填,用于BMI推算指寸) */
-    private BigDecimal height;
-
-    /** 体重(kg,选填,用于BMI推算指寸) */
-    private BigDecimal weight;
-
-    /** 1寸指宽(cm,选填) */
-    private BigDecimal fingerWidth1;
-
-    /** 1.5寸指宽(cm,选填) */
-    private BigDecimal fingerWidth15;
-
-    /** 3寸指宽(cm,选填) */
-    private BigDecimal fingerWidth3;
-
-    /** 用户穴位表ID */
-    private Long acupointTableId;
-}

+ 0 - 34
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/UserProfileQueryDTO.java

@@ -1,34 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-/**
- * 子用户列表查询条件 DTO
- */
-@Data
-public class UserProfileQueryDTO {
-
-    /** 姓名或手机号(模糊搜索) */
-    private String keyword;
-
-    /** 设备编号(模糊搜索) */
-    private String deviceCode;
-
-    /** 性别:1=男,2=女,不传则查全部 */
-    private Integer gender;
-
-    /** 注册时间开始(格式:yyyy-MM-dd HH:mm:ss) */
-    private String startTime;
-
-    /** 注册时间结束(格式:yyyy-MM-dd HH:mm:ss) */
-    private String endTime;
-
-    /** 穴位表状态:1=已生成,0=未生成,不传则查全部 */
-    private Integer acupointStatus;
-
-    /** 当前页码,默认第1页 */
-    private Integer pageNum = 1;
-
-    /** 每页条数,默认10条 */
-    private Integer pageSize = 10;
-}

+ 0 - 27
code/backend/src/main/java/com/aijiuyi/admin/controller/dto/YReferenceOptionDTO.java

@@ -1,27 +0,0 @@
-package com.aijiuyi.admin.controller.dto;
-
-import lombok.Data;
-
-import java.math.BigDecimal;
-
-/**
- * 穴位Y轴医学定位术语选项
- */
-@Data
-public class YReferenceOptionDTO {
-
-    /** 下拉值 */
-    private String value;
-
-    /** 下拉展示文案 */
-    private String label;
-
-    /** 归一化Y系数(基于C7-S4脊柱长度),按指寸偏移计算的术语为空 */
-    private BigDecimal normalizedY;
-
-    /** 指寸偏移值(向下为正,向上为负),按脊柱长度计算的术语为空 */
-    private BigDecimal cunOffset;
-
-    /** 计算说明 */
-    private String description;
-}

+ 0 - 126
code/backend/src/main/java/com/aijiuyi/admin/entity/Acupoint.java

@@ -1,126 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-/**
- * 穴位信息实体
- * 对应数据库 acupoint 表
- */
-@Data
-@TableName("acupoint")
-public class Acupoint {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 穴位编码(唯一标识,如 dazhui) */
-    private String acupointCode;
-
-    /** Excel 原始序号 */
-    private String excelSerialNo;
-
-    /** 穴位名称(如:大椎穴) */
-    private String name;
-
-    /** 单/双穴(来自 Excel 原始字段) */
-    private String singleDouble;
-
-    /** 所属经络(如:督脉、足太阳膀胱经) */
-    private String meridian;
-
-    /** 身体部位(来自 Excel) */
-    private String bodyPart;
-
-    /** 部位分区(来自 Excel) */
-    private String bodyRegion;
-
-    /** 穴位定位原文(来自 Excel) */
-    private String locationDescription;
-
-    /** 相对位置原文(来自 Excel) */
-    private String relativePosition;
-
-    /** 主管疾病/主治(来自 Excel) */
-    private String indications;
-
-    /** 产品症状覆盖(来自 Excel) */
-    private String productSymptoms;
-
-    /** 取穴说明(来自 Excel) */
-    private String acupointMethod;
-
-    /** 是否为艾灸椅产品穴位:1=是,0=否 */
-    private Integer productAcupoint;
-
-    /**
-     * 功效描述(逗号分隔,如:驱寒,祛湿)
-     * 可选值:驱寒/祛湿/祛风/化瘀/活血/化痰/养颜/扶阳
-     */
-    private String effects;
-
-    /** 定位方式:当前统一为相对定位(1) */
-    private Integer locationType;
-
-    /** 相对大椎穴纵向偏移(寸) */
-    @TableField(updateStrategy = FieldStrategy.IGNORED)
-    private BigDecimal longitudeOffset;
-
-    /** 相对大椎穴横向偏移(寸,双侧穴位填写) */
-    @TableField(updateStrategy = FieldStrategy.IGNORED)
-    private BigDecimal lateralOffset;
-
-    /** Panjabi归一化Y位置系数(0~1,基于C7-S4脊柱长度) */
-    @TableField(updateStrategy = FieldStrategy.IGNORED)
-    private BigDecimal normalizedY;
-
-    /** Y轴医学定位术语(如:第3胸椎棘突下、正对第1骶后孔中) */
-    @TableField(updateStrategy = FieldStrategy.IGNORED)
-    private String yReferenceTerm;
-
-    /** 定位描述自动解析状态:parsed=已解析,review_required=待复核,unsupported=暂不支持 */
-    private String parseStatus;
-
-    /** 定位描述自动解析置信度:high=高,medium=中,low=低 */
-    private String parseConfidence;
-
-    /** 定位描述自动解析说明 */
-    private String parseMessage;
-
-    /**
-     * 侧别:1=中心,2=左侧,3=右侧,4=双侧
-     */
-    private Integer side;
-
-    /**
-     * 指寸类型:1=一寸,2=1.5寸,3=三寸
-     */
-    @TableField(updateStrategy = FieldStrategy.IGNORED)
-    private Integer fingerWidthType;
-
-    /**
-     * 适用性别:0=不限,1=仅男,2=仅女
-     */
-    private Integer gender;
-
-    /**
-     * 状态:1=活跃,0=待完善
-     */
-    private Integer status;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 41
code/backend/src/main/java/com/aijiuyi/admin/entity/AdminPermission.java

@@ -1,41 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * 管理员权限配置实体
- * 存储每个管理员可访问的前端路由路径列表(JSON格式)
- * 超级管理员(adminRole=1)不受此表限制,默认拥有全部权限
- */
-@Data
-@TableName("admin_permission")
-public class AdminPermission {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 管理员ID(关联 app_user.id) */
-    private Long adminId;
-
-    /**
-     * 可访问的路由路径(JSON数组字符串,如 ["/dashboard","/device","/plan"])
-     * 超级管理员此字段为 null 或不使用
-     */
-    private String routePaths;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 65
code/backend/src/main/java/com/aijiuyi/admin/entity/AppUser.java

@@ -1,65 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.alibaba.fastjson.annotation.JSONField;
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * 应用用户实体(统一用户表,通过 userType 区分角色)
- * 对应数据库 app_user 表
- * userType: 1=普通用户,2=专家,3=管理员
- */
-@Data
-@TableName("app_user")
-public class AppUser {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 用户名(唯一,用于后台管理员登录) */
-    private String username;
-
-    /** 密码(MD5加密存储,后台账号使用;不序列化到响应) */
-    @JSONField(serialize = false)
-    private String password;
-
-    /** 手机号(唯一,用于App端登录) */
-    private String phone;
-
-    /** 昵称 */
-    private String nickname;
-
-    /** 头像URL */
-    private String avatar;
-
-    /**
-     * 用户类型(角色):1=普通用户,2=专家,3=管理员
-     */
-    private Integer userType;
-
-    /**
-     * 管理员角色:1=超级管理员,2=运营人员,3=只读用户(仅 userType=3 时有效)
-     */
-    private Integer adminRole;
-
-    /** 状态:1=正常,0=禁用 */
-    private Integer status;
-
-    /** 最后登录时间 */
-    private LocalDateTime lastLoginTime;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 70
code/backend/src/main/java/com/aijiuyi/admin/entity/Device.java

@@ -1,70 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-import java.util.List;
-
-/**
- * 艾灸椅设备实体
- * 对应数据库 device 表
- */
-@Data
-@TableName("device")
-public class Device {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 设备编号(唯一,如:AJY001) */
-    private String deviceCode;
-
-    /** 设备名称(如:艾灸椅-001) */
-    private String deviceName;
-
-    /** 设备型号(AJY-2026 / AJY-2025) */
-    private String deviceModel;
-
-    /** 设备序列号(唯一) */
-    private String serialNo;
-
-    /** 固件版本(如:v1.2.0) */
-    private String firmwareVersion;
-
-    /** 在线状态:1=在线,0=离线 */
-    private Integer onlineStatus;
-
-    /** 最后在线时间 */
-    private LocalDateTime lastOnlineTime;
-
-    /** 备注 */
-    private String remark;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-
-    // ======================== 非数据库字段(联表查询填充)========================
-
-    /** 绑定用户数量(联表聚合) */
-    @TableField(exist = false)
-    private Integer boundUserCount;
-
-    /** 绑定用户姓名列表(联表聚合,顿号分隔) */
-    @TableField(exist = false)
-    private String boundUserNames;
-
-    /** 绑定的用户ID列表(新增/编辑表单使用) */
-    @TableField(exist = false)
-    private List<Long> userIds;
-}

+ 0 - 31
code/backend/src/main/java/com/aijiuyi/admin/entity/DeviceUserVO.java

@@ -1,31 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * 设备绑定用户视图对象
- * 用于设备用户管理页面,联表查询 user_device + user_profile 的结果
- */
-@Data
-public class DeviceUserVO {
-
-    /** 绑定记录ID(user_device.id) */
-    private Long bindId;
-
-    /** 用户档案ID(user_profile.id) */
-    private Long userId;
-
-    /** 用户姓名 */
-    private String userName;
-
-    /** 用户手机号 */
-    private String phone;
-
-    /** 绑定时间 */
-    private LocalDateTime bindTime;
-
-    /** 是否主用户:1=是,0=否 */
-    private Integer isPrimary;
-}

+ 0 - 90
code/backend/src/main/java/com/aijiuyi/admin/entity/MoxibustionTechnique.java

@@ -1,90 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-/**
- * 艾灸手法实体
- * 对应数据库 moxibustion_technique 表
- */
-@Data
-@TableName("moxibustion_technique")
-public class MoxibustionTechnique {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /**
-     * 手法编码(唯一标识,如 oneclick)
-     */
-    private String techniqueCode;
-
-    /**
-     * 手法名称(如:一键艾灸)
-     */
-    private String name;
-
-    /**
-     * 运动模式:
-     * default=默认工艺,fixed=固定不动,updown=上下移动,circular=圆周运动,dotted=点状移动
-     */
-    private String motionMode;
-
-    /**
-     * 温度范围最小值(°C,35-50)
-     */
-    private BigDecimal tempMin;
-
-    /**
-     * 温度范围最大值(°C,35-50)
-     */
-    private BigDecimal tempMax;
-
-    /**
-     * 时间范围最小值(分钟,1-30)
-     */
-    private Integer timeMin;
-
-    /**
-     * 时间范围最大值(分钟,1-30)
-     */
-    private Integer timeMax;
-
-    /**
-     * 运动幅度(mm,1-20)
-     * 运动模式非"固定不动"时必填
-     */
-    private BigDecimal motionAmplitude;
-
-    /**
-     * 运动速度(mm/s,1-10)
-     * 运动模式非"固定不动"时必填
-     */
-    private BigDecimal motionSpeed;
-
-    /**
-     * 状态:1=启用,0=禁用
-     */
-    private Integer status;
-
-    /**
-     * 备注(最长200字符)
-     */
-    private String remark;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 90
code/backend/src/main/java/com/aijiuyi/admin/entity/Plan.java

@@ -1,90 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-/**
- * 方案实体
- * 对应数据库 plan 表
- */
-@Data
-@TableName("plan")
-public class Plan {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 方案编码(唯一标识,如 S001) */
-    private String planCode;
-
-    /** 方案名称(最长100字符) */
-    private String name;
-
-    /**
-     * 模式类型:1=一键艾灸,2=专业模式,3=自定义模式,4=延年圣手
-     */
-    private Integer modeType;
-
-    /**
-     * 功效类型:驱寒/祛湿/祛风/化瘀/活血/化痰/养颜/扶阳
-     */
-    private String effectType;
-
-    /** 适用症状(逗号分隔:怕冷,感冒,咳嗽,...) */
-    private String symptoms;
-
-    /**
-     * 适用人群:0=不限,1=仅男,2=仅女
-     */
-    private Integer applicableGender;
-
-    /** 适用年龄最小值(岁) */
-    private Integer ageMin;
-
-    /** 适用年龄最大值(岁) */
-    private Integer ageMax;
-
-    /** 方案描述(最长200字符) */
-    private String description;
-
-    /**
-     * 创作人类型:1=管理员,2=普通用户,3=专家
-     */
-    private Integer authorType;
-
-    /** 创作人ID(对应 app_user.id) */
-    private Long authorId;
-
-    /** 创作人名称(冗余存储,加速查询) */
-    private String authorName;
-
-    /**
-     * 状态:0=草稿,1=已发布,2=已下架
-     */
-    private Integer status;
-
-    /** 使用人数 */
-    private Integer useCount;
-
-    /** 平均评分 */
-    private BigDecimal avgRating;
-
-    /** 最后使用时间 */
-    private LocalDateTime lastUseTime;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 64
code/backend/src/main/java/com/aijiuyi/admin/entity/PlanStep.java

@@ -1,64 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-/**
- * 方案步骤实体
- * 对应数据库 plan_step 表
- */
-@Data
-@TableName("plan_step")
-public class PlanStep {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 方案ID(关联 plan.id) */
-    private Long planId;
-
-    /** 步骤序号(从1开始) */
-    private Integer stepOrder;
-
-    /** 穴位ID(关联 acupoint.id) */
-    private Long acupointId;
-
-    /** 穴位名称(冗余存储) */
-    private String acupointName;
-
-    /**
-     * 侧别:1=中心,2=左侧,3=右侧,4=双侧
-     */
-    private Integer side;
-
-    /** 温度(°C,35-50) */
-    private BigDecimal temperature;
-
-    /** 时间(分钟,1-30) */
-    private Integer duration;
-
-    /** 艾灸手法ID(关联 moxibustion_technique.id) */
-    private Long techniqueId;
-
-    /** 艾灸手法名称(冗余存储) */
-    private String techniqueName;
-
-    /** 备注(最长100字符) */
-    private String remark;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 52
code/backend/src/main/java/com/aijiuyi/admin/entity/SysUser.java

@@ -1,52 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * 系统用户实体
- * 对应数据库 sys_user 表
- */
-@Data
-@TableName("sys_user")
-public class SysUser {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 用户名(唯一,用于登录) */
-    private String username;
-
-    /** 密码(MD5加密存储,不允许返回给前端) */
-    private String password;
-
-    /** 昵称(显示名称) */
-    private String nickname;
-
-    /** 邮箱 */
-    private String email;
-
-    /** 手机号 */
-    private String phone;
-
-    /** 头像URL */
-    private String avatar;
-
-    /** 状态:1=正常,0=禁用 */
-    private Integer status;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 62
code/backend/src/main/java/com/aijiuyi/admin/entity/UserAcupoint.java

@@ -1,62 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-/**
- * 用户穴位坐标实体
- * 对应数据库 user_acupoint 表
- * 根据用户体型数据(指宽/肩宽/身长)与穴位定位参数计算生成
- */
-@Data
-@TableName("user_acupoint")
-public class UserAcupoint {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 子用户ID(关联 user_profile.id) */
-    private Long userId;
-
-    /** 穴位ID(关联 acupoint.id) */
-    private Long acupointId;
-
-    /** 穴位名称(冗余存储,加速查询) */
-    private String acupointName;
-
-    /**
-     * 侧别:center=中心,left=左侧,right=右侧
-     * 双侧穴位(acupoint.side=4)会生成两条记录(left+right)
-     */
-    private String acupointSide;
-
-    /** 相对大椎穴纵向偏移(寸) */
-    private BigDecimal cunValue;
-
-    /** 指寸类型:1寸/1.5寸/3寸 */
-    private String cunType;
-
-    /** 计算后的X坐标(mm,相对大椎穴中心,向右为正) */
-    private BigDecimal coordinateX;
-
-    /** 计算后的Y坐标(mm,相对大椎穴中心,向下为正) */
-    private BigDecimal coordinateY;
-
-    /** 计算后的Z坐标(mm,体表=0) */
-    private BigDecimal coordinateZ;
-
-    /** 置信度:high=高/medium=中/low=低 */
-    private String confidence;
-
-    /** 首次生成时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 最后更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 55
code/backend/src/main/java/com/aijiuyi/admin/entity/UserDevice.java

@@ -1,55 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * 用户设备绑定实体
- * 对应数据库 user_device 表
- */
-@Data
-@TableName("user_device")
-public class UserDevice {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 所属子用户ID(关联 user_profile.id) */
-    private Long userId;
-
-    /** 设备唯一编码 */
-    private String deviceCode;
-
-    /** 设备名称 */
-    private String deviceName;
-
-    /** 设备型号 */
-    private String deviceModel;
-
-    /** 是否主设备:1=是,0=否 */
-    private Integer isPrimary;
-
-    /** 在线状态:1=在线,0=离线 */
-    private Integer onlineStatus;
-
-    /** 绑定状态:1=已绑定,0=已解绑 */
-    private Integer status;
-
-    /** 绑定时间 */
-    private LocalDateTime bindTime;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 61
code/backend/src/main/java/com/aijiuyi/admin/entity/UserPlan.java

@@ -1,61 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-/**
- * 用户方案实体
- * 对应数据库 user_plan 表
- * 记录用户个性化工艺参数方案
- */
-@Data
-@TableName("user_plan")
-public class UserPlan {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 子用户ID(关联 user_profile.id) */
-    private Long userId;
-
-    /** 方案ID(关联 plan.id) */
-    private Long planId;
-
-    /** 方案编码(冗余存储) */
-    private String planCode;
-
-    /** 方案名称(冗余存储) */
-    private String planName;
-
-    /**
-     * 模式类型:1=一键艾灸,2=专业模式,3=自定义模式,4=延年圣手
-     */
-    private Integer modeType;
-
-    /** 创作人名称(冗余存储) */
-    private String authorName;
-
-    /** 步骤数量 */
-    private Integer stepCount;
-
-    /** 使用次数 */
-    private Integer useCount;
-
-    /** 个性化工艺步骤(JSON格式,含坐标/温度/时间等) */
-    private String stepsJson;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 订阅/生成时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-}

+ 0 - 81
code/backend/src/main/java/com/aijiuyi/admin/entity/UserProfile.java

@@ -1,81 +0,0 @@
-package com.aijiuyi.admin.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-/**
- * 子用户信息实体
- * 对应数据库 user_profile 表
- */
-@Data
-@TableName("user_profile")
-public class UserProfile {
-
-    /** 主键ID */
-    @TableId(type = IdType.AUTO)
-    private Long id;
-
-    /** 所属应用用户ID(关联 app_user.id) */
-    private Long userId;
-
-    /** 姓名 */
-    private String name;
-
-    /** 性别:1=男,2=女 */
-    private Integer gender;
-
-    /** 年龄 */
-    private Integer age;
-
-    /** 手机号(11位,唯一) */
-    private String phone;
-
-    /** 肩宽(cm) */
-    private BigDecimal shoulderWidth;
-
-    /** 身长(cm)(坐高) */
-    private BigDecimal bodyHeight;
-
-    /** 脊柱长度C7-S4(cm,30-60) */
-    private BigDecimal spineLength;
-
-    /** 身高(cm) */
-    private BigDecimal height;
-
-    /** 体重(kg) */
-    private BigDecimal weight;
-
-    /** 1寸指宽(cm,选填,提高穴位精度) */
-    @TableField("finger_width_1")
-    private BigDecimal fingerWidth1;
-
-    /** 1.5寸指宽(cm,选填) */
-    @TableField("finger_width_15")
-    private BigDecimal fingerWidth15;
-
-    /** 3寸指宽(cm,选填) */
-    @TableField("finger_width_3")
-    private BigDecimal fingerWidth3;
-
-    /** 用户穴位表ID */
-    private Long acupointTableId;
-
-    /** 逻辑删除:0=未删除,1=已删除 */
-    @TableLogic
-    private Integer deleted;
-
-    /** 创建时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createTime;
-
-    /** 更新时间(自动填充) */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updateTime;
-
-    /** 绑定设备编号(逗号分隔,非数据库字段,查询时JOIN填充) */
-    @TableField(exist = false)
-    private String deviceCodes;
-}

+ 0 - 12
code/backend/src/main/java/com/aijiuyi/admin/mapper/AdminPermissionMapper.java

@@ -1,12 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.entity.AdminPermission;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * 管理员权限配置 Mapper
- */
-@Mapper
-public interface AdminPermissionMapper extends BaseMapper<AdminPermission> {
-}

+ 0 - 36
code/backend/src/main/java/com/aijiuyi/admin/mapper/DeviceMapper.java

@@ -1,36 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.controller.dto.DeviceQueryDTO;
-import com.aijiuyi.admin.entity.Device;
-import com.aijiuyi.admin.entity.DeviceUserVO;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
-
-/**
- * 设备管理 Mapper
- */
-@Mapper
-public interface DeviceMapper extends BaseMapper<Device> {
-
-    /**
-     * 分页查询设备列表(联表聚合绑定用户数和用户姓名)
-     *
-     * @param page     分页参数
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<Device> pageWithUsers(Page<Device> page, @Param("q") DeviceQueryDTO queryDTO);
-
-    /**
-     * 根据设备编号查询绑定的用户列表
-     *
-     * @param deviceCode 设备编号
-     * @return 绑定用户列表
-     */
-    List<DeviceUserVO> getUsersByDeviceCode(@Param("deviceCode") String deviceCode);
-}

+ 0 - 12
code/backend/src/main/java/com/aijiuyi/admin/mapper/MoxibustionTechniqueMapper.java

@@ -1,12 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.entity.MoxibustionTechnique;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * 艾灸手法 Mapper 接口
- */
-@Mapper
-public interface MoxibustionTechniqueMapper extends BaseMapper<MoxibustionTechnique> {
-}

+ 0 - 12
code/backend/src/main/java/com/aijiuyi/admin/mapper/PlanMapper.java

@@ -1,12 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.entity.Plan;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * 方案 Mapper 接口
- */
-@Mapper
-public interface PlanMapper extends BaseMapper<Plan> {
-}

+ 0 - 12
code/backend/src/main/java/com/aijiuyi/admin/mapper/PlanStepMapper.java

@@ -1,12 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.entity.PlanStep;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * 方案步骤 Mapper 接口
- */
-@Mapper
-public interface PlanStepMapper extends BaseMapper<PlanStep> {
-}

+ 0 - 36
code/backend/src/main/java/com/aijiuyi/admin/mapper/SysLogMapper.java

@@ -1,36 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.common.entity.SysLog;
-import com.aijiuyi.admin.controller.dto.SysLogQueryDTO;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
-
-/**
- * 系统日志 Mapper
- * 基本 CRUD 由 MyBatis Plus BaseMapper 提供,复杂查询在 SysLogMapper.xml 中定义
- */
-@Mapper
-public interface SysLogMapper extends BaseMapper<SysLog> {
-
-    /**
-     * 分页查询操作日志(含多条件筛选)
-     *
-     * @param page 分页参数
-     * @param dto  查询条件
-     * @return 分页结果
-     */
-    IPage<SysLog> selectLogPage(Page<SysLog> page, @Param("dto") SysLogQueryDTO dto);
-
-    /**
-     * 查询全部操作日志(用于 Excel 导出)
-     *
-     * @param dto 查询条件
-     * @return 日志列表
-     */
-    List<SysLog> selectLogList(@Param("dto") SysLogQueryDTO dto);
-}

+ 0 - 13
code/backend/src/main/java/com/aijiuyi/admin/mapper/SysUserMapper.java

@@ -1,13 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.entity.SysUser;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * 系统用户 Mapper
- * 基本 CRUD 由 MyBatis Plus BaseMapper 提供,复杂查询在 SysUserMapper.xml 中定义
- */
-@Mapper
-public interface SysUserMapper extends BaseMapper<SysUser> {
-}

+ 0 - 38
code/backend/src/main/java/com/aijiuyi/admin/mapper/UserAcupointMapper.java

@@ -1,38 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.entity.UserAcupoint;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
-
-/**
- * 用户穴位坐标 Mapper
- */
-@Mapper
-public interface UserAcupointMapper extends BaseMapper<UserAcupoint> {
-
-    /**
-     * 查询指定子用户的所有穴位坐标列表
-     *
-     * @param userId 子用户ID(user_profile.id)
-     * @return 穴位坐标列表
-     */
-    List<UserAcupoint> listByUserId(@Param("userId") Long userId);
-
-    /**
-     * 删除指定子用户的所有穴位坐标记录(物理删除,用于重新生成前清空)
-     *
-     * @param userId 子用户ID
-     */
-    void deleteByUserId(@Param("userId") Long userId);
-
-    /**
-     * 删除指定子用户下某穴位的坐标记录(物理删除,用于按穴位局部更新)
-     *
-     * @param userId     子用户ID
-     * @param acupointId 穴位ID
-     */
-    void deleteByUserIdAndAcupointId(@Param("userId") Long userId, @Param("acupointId") Long acupointId);
-}

+ 0 - 12
code/backend/src/main/java/com/aijiuyi/admin/mapper/UserDeviceMapper.java

@@ -1,12 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.entity.UserDevice;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * 用户设备绑定 Mapper
- */
-@Mapper
-public interface UserDeviceMapper extends BaseMapper<UserDevice> {
-}

+ 0 - 22
code/backend/src/main/java/com/aijiuyi/admin/mapper/UserPlanMapper.java

@@ -1,22 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.controller.dto.UserPlanQueryDTO;
-import com.aijiuyi.admin.entity.UserPlan;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
-
-/**
- * 用户方案 Mapper 接口
- */
-@Mapper
-public interface UserPlanMapper extends BaseMapper<UserPlan> {
-
-    IPage<UserPlan> pageList(Page<UserPlan> page, @Param("query") UserPlanQueryDTO queryDTO);
-
-    List<UserPlan> getPlansByUserId(@Param("userId") Long userId);
-}

+ 0 - 26
code/backend/src/main/java/com/aijiuyi/admin/mapper/UserProfileMapper.java

@@ -1,26 +0,0 @@
-package com.aijiuyi.admin.mapper;
-
-import com.aijiuyi.admin.controller.dto.UserProfileQueryDTO;
-import com.aijiuyi.admin.entity.UserProfile;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-/**
- * 子用户信息 Mapper
- */
-@Mapper
-public interface UserProfileMapper extends BaseMapper<UserProfile> {
-
-    /**
-     * 分页查询子用户列表(联表获取绑定设备编号)
-     *
-     * @param page     分页对象
-     * @param queryDTO 查询条件
-     * @return 分页结果(含设备编号字段 deviceCodes)
-     */
-    IPage<UserProfile> pageWithDevices(Page<UserProfile> page, @Param("q") UserProfileQueryDTO queryDTO);
-}
-

+ 0 - 65
code/backend/src/main/java/com/aijiuyi/admin/service/AcupointService.java

@@ -1,65 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.AcupointQueryDTO;
-import com.aijiuyi.admin.controller.dto.YReferenceOptionDTO;
-import com.aijiuyi.admin.entity.Acupoint;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/**
- * 穴位管理 Service 接口
- */
-public interface AcupointService extends IService<Acupoint> {
-
-    /**
-     * 分页查询穴位列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<Acupoint> pageList(AcupointQueryDTO queryDTO);
-
-    /**
-     * 获取可用于穴位Y轴计算的医学定位术语选项
-     *
-     * @return Y轴术语选项
-     */
-    List<YReferenceOptionDTO> listYReferenceOptions();
-
-    /**
-     * 新增穴位
-     *
-     * @param acupoint 穴位信息
-     */
-    void addAcupoint(Acupoint acupoint);
-
-    /**
-     * 修改穴位信息
-     *
-     * @param acupoint 穴位信息
-     */
-    void updateAcupoint(Acupoint acupoint);
-
-    /**
-     * 删除穴位(逻辑删除)
-     *
-     * @param id 穴位ID
-     */
-    void deleteAcupoint(Long id);
-
-    /**
-     * 批量删除穴位(逻辑删除)
-     *
-     * @param ids 穴位ID列表
-     */
-    void batchDeleteAcupoint(List<Long> ids);
-
-    /**
-     * 批量导入穴位
-     *
-     * @param list 穴位信息列表
-     */
-    void batchImport(List<Acupoint> list);
-}

+ 0 - 83
code/backend/src/main/java/com/aijiuyi/admin/service/AdminService.java

@@ -1,83 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.AdminQueryDTO;
-import com.aijiuyi.admin.entity.AppUser;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/**
- * 管理员管理 Service 接口
- * 对 app_user 表中 userType=3 的管理员进行管理,
- * 同时提供管理员权限配置的查询与更新
- */
-public interface AdminService extends IService<AppUser> {
-
-    /**
-     * 分页查询管理员列表
-     *
-     * @param queryDTO 查询参数
-     * @return 分页结果
-     */
-    IPage<AppUser> pageList(AdminQueryDTO queryDTO);
-
-    /**
-     * 新增管理员
-     * 对密码进行 MD5 加密,校验用户名唯一性
-     *
-     * @param admin 管理员信息
-     */
-    void addAdmin(AppUser admin);
-
-    /**
-     * 修改管理员信息
-     * 若传入密码则重新加密,否则保留原密码
-     *
-     * @param admin 管理员信息(需携带 id)
-     */
-    void updateAdmin(AppUser admin);
-
-    /**
-     * 删除管理员(逻辑删除)
-     * 超级管理员不可删除
-     *
-     * @param id 管理员ID
-     */
-    void deleteAdmin(Long id);
-
-    /**
-     * 批量删除管理员(逻辑删除)
-     * 超级管理员不可删除
-     *
-     * @param ids 管理员ID列表
-     */
-    void batchDeleteAdmin(List<Long> ids);
-
-    /**
-     * 修改管理员状态(禁用/启用)
-     * 不允许禁用当前登录账号自身
-     *
-     * @param id     管理员ID
-     * @param status 目标状态:1=启用,0=禁用
-     */
-    void updateStatus(Long id, Integer status);
-
-    /**
-     * 查询管理员权限路由列表
-     * 超级管理员返回 null(前端解释为全部权限)
-     *
-     * @param adminId 管理员ID
-     * @return 路由路径列表(JSON数组字符串,如 ["/dashboard","/device"]);超级管理员返回 null
-     */
-    String getPermissions(Long adminId);
-
-    /**
-     * 更新管理员权限路由列表
-     * 若超级管理员调用此方法,视为无效操作直接返回
-     *
-     * @param adminId    管理员ID
-     * @param routePaths 路由路径列表(JSON数组字符串)
-     */
-    void updatePermissions(Long adminId, String routePaths);
-}

+ 0 - 71
code/backend/src/main/java/com/aijiuyi/admin/service/AppUserService.java

@@ -1,71 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.controller.dto.AppUserQueryDTO;
-import com.aijiuyi.admin.controller.dto.LoginDTO;
-import com.aijiuyi.admin.entity.AppUser;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.Map;
-
-/**
- * 应用用户 Service
- * 统一用户中心:普通用户、专家、管理员均使用此表,通过 userType 区分
- */
-public interface AppUserService extends IService<AppUser> {
-
-    /**
-     * 管理员登录
-     * 通过用户名+密码登录,返回 Token 和用户信息
-     *
-     * @param loginDTO 登录参数(用户名、密码)
-     * @return 包含 token 和用户基本信息的 Result
-     */
-    Result<Map<String, Object>> login(LoginDTO loginDTO);
-
-    /**
-     * 退出登录
-     * 从 Redis 中删除 Token,使 Token 立即失效
-     *
-     * @param token 当前用户的 Token(从请求头中获取)
-     */
-    void logout(String token);
-
-    /**
-     * 分页查询应用用户列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<AppUser> pageList(AppUserQueryDTO queryDTO);
-
-    /**
-     * 修改用户状态(禁用/启用)
-     *
-     * @param id     用户ID
-     * @param status 目标状态:1=启用,0=禁用
-     */
-    void updateStatus(Long id, Integer status);
-
-    /**
-     * 新增应用用户
-     *
-     * @param appUser 用户信息
-     */
-    void addUser(AppUser appUser);
-
-    /**
-     * 修改应用用户
-     *
-     * @param appUser 用户信息
-     */
-    void updateUser(AppUser appUser);
-
-    /**
-     * 删除应用用户(逻辑删除)
-     *
-     * @param id 用户ID
-     */
-    void deleteUser(Long id);
-}

+ 0 - 99
code/backend/src/main/java/com/aijiuyi/admin/service/DeviceService.java

@@ -1,99 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.DeviceQueryDTO;
-import com.aijiuyi.admin.controller.dto.DeviceSaveDTO;
-import com.aijiuyi.admin.entity.Device;
-import com.aijiuyi.admin.entity.DeviceUserVO;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/**
- * 设备管理 Service 接口
- */
-public interface DeviceService extends IService<Device> {
-
-    /**
-     * 分页查询设备列表(联表聚合绑定用户信息)
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<Device> pageList(DeviceQueryDTO queryDTO);
-
-    /**
-     * 新增设备(可同时绑定初始用户)
-     *
-     * @param dto 设备信息
-     */
-    void addDevice(DeviceSaveDTO dto);
-
-    /**
-     * 修改设备信息(可同步更新绑定用户)
-     *
-     * @param dto 设备信息
-     */
-    void updateDevice(DeviceSaveDTO dto);
-
-    /**
-     * 删除设备(逻辑删除,同步解绑所有用户)
-     *
-     * @param id 设备ID
-     */
-    void deleteDevice(Long id);
-
-    /**
-     * 批量删除设备(逻辑删除,同步解绑所有用户)
-     *
-     * @param ids 设备ID列表
-     */
-    void batchDeleteDevices(List<Long> ids);
-
-    /**
-     * 批量导入设备(跳过编号已存在的记录)
-     *
-     * @param list 设备信息列表
-     */
-    void batchImport(List<Device> list);
-
-    /**
-     * 查询设备绑定的用户列表
-     *
-     * @param deviceId 设备ID
-     * @return 绑定用户列表
-     */
-    List<DeviceUserVO> getDeviceUsers(Long deviceId);
-
-    /**
-     * 为设备添加绑定用户
-     *
-     * @param deviceId  设备ID
-     * @param profileId 用户档案ID(user_profile.id)
-     */
-    void addDeviceUser(Long deviceId, Long profileId);
-
-    /**
-     * 解绑设备用户
-     *
-     * @param deviceId  设备ID
-     * @param profileId 用户档案ID
-     */
-    void removeDeviceUser(Long deviceId, Long profileId);
-
-    /**
-     * 设为主用户(同一设备下只能有一个主用户)
-     *
-     * @param deviceId  设备ID
-     * @param profileId 用户档案ID
-     */
-    void setDeviceUserPrimary(Long deviceId, Long profileId);
-
-    /**
-     * 批量解绑设备用户
-     *
-     * @param deviceId 设备ID
-     * @param bindIds  绑定记录ID列表(user_device.id)
-     */
-    void batchRemoveDeviceUsers(Long deviceId, List<Long> bindIds);
-}

+ 0 - 50
code/backend/src/main/java/com/aijiuyi/admin/service/MoxibustionTechniqueService.java

@@ -1,50 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.MoxibustionTechniqueQueryDTO;
-import com.aijiuyi.admin.entity.MoxibustionTechnique;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/**
- * 艾灸手法管理 Service 接口
- */
-public interface MoxibustionTechniqueService extends IService<MoxibustionTechnique> {
-
-    /**
-     * 分页查询艾灸手法列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<MoxibustionTechnique> pageList(MoxibustionTechniqueQueryDTO queryDTO);
-
-    /**
-     * 新增艾灸手法
-     *
-     * @param technique 手法信息
-     */
-    void addTechnique(MoxibustionTechnique technique);
-
-    /**
-     * 修改艾灸手法信息
-     *
-     * @param technique 手法信息
-     */
-    void updateTechnique(MoxibustionTechnique technique);
-
-    /**
-     * 删除艾灸手法(逻辑删除)
-     *
-     * @param id 手法ID
-     */
-    void deleteTechnique(Long id);
-
-    /**
-     * 批量删除艾灸手法(逻辑删除)
-     *
-     * @param ids 手法ID列表
-     */
-    void batchDeleteTechnique(List<Long> ids);
-}

+ 0 - 96
code/backend/src/main/java/com/aijiuyi/admin/service/PlanService.java

@@ -1,96 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.PlanQueryDTO;
-import com.aijiuyi.admin.controller.dto.PlanSaveDTO;
-import com.aijiuyi.admin.entity.Plan;
-import com.aijiuyi.admin.entity.PlanStep;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/**
- * 方案管理 Service 接口
- */
-public interface PlanService extends IService<Plan> {
-
-    /**
-     * 分页查询方案列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<Plan> pageList(PlanQueryDTO queryDTO);
-
-    /**
-     * 查询方案详情(含步骤列表)
-     *
-     * @param id 方案ID
-     * @return 方案详情
-     */
-    PlanSaveDTO getDetail(Long id);
-
-    /**
-     * 新增方案(含步骤)
-     *
-     * @param dto 方案信息
-     */
-    void addPlan(PlanSaveDTO dto);
-
-    /**
-     * 修改方案(含步骤)
-     *
-     * @param dto 方案信息
-     */
-    void updatePlan(PlanSaveDTO dto);
-
-    /**
-     * 删除方案(逻辑删除,仅草稿状态可删)
-     *
-     * @param id 方案ID
-     */
-    void deletePlan(Long id);
-
-    /**
-     * 批量删除方案(仅草稿状态可删)
-     *
-     * @param ids 方案ID列表
-     */
-    void batchDeletePlan(List<Long> ids);
-
-    /**
-     * 发布方案(草稿 → 已发布)
-     *
-     * @param id 方案ID
-     */
-    void publishPlan(Long id);
-
-    /**
-     * 批量发布方案
-     *
-     * @param ids 方案ID列表
-     */
-    void batchPublishPlan(List<Long> ids);
-
-    /**
-     * 下架方案(已发布 → 已下架)
-     *
-     * @param id 方案ID
-     */
-    void offshelfPlan(Long id);
-
-    /**
-     * 批量下架方案
-     *
-     * @param ids 方案ID列表
-     */
-    void batchOffshelfPlan(List<Long> ids);
-
-    /**
-     * 查询方案步骤列表
-     *
-     * @param planId 方案ID
-     * @return 步骤列表
-     */
-    List<PlanStep> getStepsByPlanId(Long planId);
-}

+ 0 - 19
code/backend/src/main/java/com/aijiuyi/admin/service/SimulationService.java

@@ -1,19 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.SimulationResponse;
-
-/**
- * 方案模拟测试 Service 接口
- * 组装推送数据:用户体型 + 方案步骤 + 穴位坐标偏移
- */
-public interface SimulationService {
-
-    /**
-     * 组装方案模拟推送数据
-     *
-     * @param profileId 子用户ID(user_profile.id)
-     * @param planId    方案ID(plan.id)
-     * @return 模拟推送数据
-     */
-    SimulationResponse assemblePushData(Long profileId, Long planId);
-}

+ 0 - 31
code/backend/src/main/java/com/aijiuyi/admin/service/SysUserService.java

@@ -1,31 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.controller.dto.LoginDTO;
-import com.aijiuyi.admin.entity.SysUser;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.Map;
-
-/**
- * 系统用户 Service 接口
- */
-public interface SysUserService extends IService<SysUser> {
-
-    /**
-     * 用户登录
-     * 验证用户名和密码,生成 Token 并存入 Redis
-     *
-     * @param loginDTO 登录参数(用户名、密码)
-     * @return token 和用户基本信息
-     */
-    Result<Map<String, Object>> login(LoginDTO loginDTO);
-
-    /**
-     * 用户退出登录
-     * 从 Redis 中删除当前用户的 Token
-     *
-     * @param token 当前用户的 Token(从请求头中获取)
-     */
-    void logout(String token);
-}

+ 0 - 37
code/backend/src/main/java/com/aijiuyi/admin/service/UserAcupointService.java

@@ -1,37 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.entity.UserAcupoint;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/**
- * 用户穴位坐标 Service 接口
- * 提供穴位坐标生成、查询功能
- */
-public interface UserAcupointService extends IService<UserAcupoint> {
-
-    /**
-     * 为指定子用户生成(或重新生成)全量穴位坐标
-     * 生成逻辑:先清除该用户所有旧记录,再根据当前全量活跃穴位重新计算并插入
-     *
-     * @param profileId 子用户ID(user_profile.id)
-     */
-    void generateForUser(Long profileId);
-
-    /**
-     * 当穴位管理新增一条穴位时,为所有子用户追加该穴位的坐标记录
-     * 如果该用户尚未生成过穴位表(user_profile.acupoint_table_id 为空),则跳过
-     *
-     * @param acupointId 新增的穴位ID
-     */
-    void generateForAllUsersByAcupoint(Long acupointId);
-
-    /**
-     * 查询指定子用户的穴位坐标列表
-     *
-     * @param profileId 子用户ID
-     * @return 穴位坐标列表
-     */
-    List<UserAcupoint> listByUser(Long profileId);
-}

+ 0 - 59
code/backend/src/main/java/com/aijiuyi/admin/service/UserDeviceService.java

@@ -1,59 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.UserDeviceQueryDTO;
-import com.aijiuyi.admin.entity.UserDevice;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/**
- * 用户设备绑定 Service
- */
-public interface UserDeviceService extends IService<UserDevice> {
-
-    /**
-     * 分页查询设备列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<UserDevice> pageList(UserDeviceQueryDTO queryDTO);
-
-    /**
-     * 绑定设备
-     *
-     * @param device 设备信息
-     */
-    void bindDevice(UserDevice device);
-
-    /**
-     * 解绑设备
-     *
-     * @param id 设备记录ID
-     */
-    void unbindDevice(Long id);
-
-    /**
-     * 删除设备记录
-     *
-     * @param id 设备记录ID
-     */
-    void deleteDevice(Long id);
-
-    /**
-     * 设置主设备(同一用户下只有一个主设备)
-     *
-     * @param id     设备记录ID
-     * @param userId 所属子用户ID
-     */
-    void setPrimaryDevice(Long id, Long userId);
-
-    /**
-     * 批量解绑设备
-     *
-     * @param ids 设备记录ID列表
-     */
-    void batchUnbindDevices(List<Long> ids);
-}
-

+ 0 - 33
code/backend/src/main/java/com/aijiuyi/admin/service/UserPlanService.java

@@ -1,33 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.UserPlanQueryDTO;
-import com.aijiuyi.admin.entity.UserPlan;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * 用户方案 Service 接口
- */
-public interface UserPlanService extends IService<UserPlan> {
-
-    IPage<UserPlan> pageList(UserPlanQueryDTO queryDTO);
-
-    /**
-     * 根据用户ID分页查询方案列表
-     *
-     * @param userId   用户ID
-     * @param pageNum  当前页码(从1开始)
-     * @param pageSize 每页条数
-     * @return 分页结果
-     */
-    IPage<UserPlan> pageByUserId(Long userId, Integer pageNum, Integer pageSize);
-
-    /**
-     * 查询用户方案详情
-     *
-     * @param id 用户方案ID
-     * @return 用户方案详情
-     */
-    UserPlan getDetail(Long id);
-
-}

+ 0 - 67
code/backend/src/main/java/com/aijiuyi/admin/service/UserProfileService.java

@@ -1,67 +0,0 @@
-package com.aijiuyi.admin.service;
-
-import com.aijiuyi.admin.controller.dto.UserProfileDTO;
-import com.aijiuyi.admin.controller.dto.UserProfileQueryDTO;
-import com.aijiuyi.admin.entity.UserProfile;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/**
- * 子用户信息 Service
- */
-public interface UserProfileService extends IService<UserProfile> {
-
-    /**
-     * 分页查询子用户列表(联表获取设备编号)
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    IPage<UserProfile> pageList(UserProfileQueryDTO queryDTO);
-
-    /**
-     * 新增子用户
-     *
-     * @param dto 子用户信息 DTO
-     */
-    void addProfile(UserProfileDTO dto);
-
-    /**
-     * 修改子用户
-     *
-     * @param dto 子用户信息 DTO
-     */
-    void updateProfile(UserProfileDTO dto);
-
-    /**
-     * 删除子用户
-     *
-     * @param id 子用户ID
-     */
-    void deleteProfile(Long id);
-
-    /**
-     * 批量删除子用户
-     *
-     * @param ids 子用户ID列表
-     */
-    void batchDelete(List<Long> ids);
-
-    /**
-     * 批量导入子用户
-     *
-     * @param list 子用户信息列表
-     */
-    void batchImport(List<UserProfileDTO> list);
-
-    /**
-     * 查询用户档案列表(用于下拉选择,支持姓名/手机号模糊搜索,最多返回200条)
-     *
-     * @param keyword 搜索关键字(姓名或手机号,为空则返回全部)
-     * @return 用户档案列表
-     */
-    List<UserProfile> listOptions(String keyword);
-}
-

+ 0 - 217
code/backend/src/main/java/com/aijiuyi/admin/service/impl/AcupointServiceImpl.java

@@ -1,217 +0,0 @@
-package com.aijiuyi.admin.service.impl;
-
-import com.aijiuyi.admin.common.constant.ResultCode;
-import com.aijiuyi.admin.common.exception.BusinessException;
-import com.aijiuyi.admin.common.util.AcupointLocationParser;
-import com.aijiuyi.admin.common.util.AcupointYReference;
-import com.aijiuyi.admin.common.util.LogUtil;
-import com.aijiuyi.admin.controller.dto.AcupointQueryDTO;
-import com.aijiuyi.admin.controller.dto.YReferenceOptionDTO;
-import com.aijiuyi.admin.entity.Acupoint;
-import com.aijiuyi.admin.mapper.AcupointMapper;
-import com.aijiuyi.admin.service.AcupointService;
-import com.aijiuyi.admin.service.UserAcupointService;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.StringUtils;
-
-import java.util.List;
-
-/**
- * 穴位管理 Service 实现类
- */
-@Service
-public class AcupointServiceImpl extends ServiceImpl<AcupointMapper, Acupoint> implements AcupointService {
-
-    /** 用户穴位坐标服务(懒加载避免循环依赖) */
-    @Lazy
-    @Autowired
-    private UserAcupointService userAcupointService;
-
-    /**
-     * 分页查询穴位列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @Override
-    public IPage<Acupoint> pageList(AcupointQueryDTO queryDTO) {
-        Page<Acupoint> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
-        LambdaQueryWrapper<Acupoint> wrapper = new LambdaQueryWrapper<>();
-        wrapper.like(StringUtils.hasText(queryDTO.getName()), Acupoint::getName, queryDTO.getName())
-               .eq(StringUtils.hasText(queryDTO.getMeridian()), Acupoint::getMeridian, queryDTO.getMeridian())
-               .eq(queryDTO.getLocationType() != null, Acupoint::getLocationType, queryDTO.getLocationType())
-               .eq(queryDTO.getStatus() != null, Acupoint::getStatus, queryDTO.getStatus())
-               .orderByDesc(Acupoint::getCreateTime);
-        return page(page, wrapper);
-    }
-
-    @Override
-    public List<YReferenceOptionDTO> listYReferenceOptions() {
-        return AcupointYReference.listOptions();
-    }
-
-    /**
-     * 新增穴位,校验编码和名称唯一性
-     *
-     * @param acupoint 穴位信息
-     */
-    @Override
-    public void addAcupoint(Acupoint acupoint) {
-        normalizeLocationFields(acupoint);
-        // 穴位编码唯一性校验
-        long codeCount = lambdaQuery().eq(Acupoint::getAcupointCode, acupoint.getAcupointCode()).count();
-        if (codeCount > 0) {
-            throw new BusinessException(ResultCode.ACUPOINT_CODE_EXISTS);
-        }
-        // 穴位名称唯一性校验
-        long nameCount = lambdaQuery().eq(Acupoint::getName, acupoint.getName()).count();
-        if (nameCount > 0) {
-            throw new BusinessException(ResultCode.ACUPOINT_NAME_EXISTS);
-        }
-        save(acupoint);
-        LogUtil.info(AcupointServiceImpl.class, "新增穴位[{}],编码[{}]", acupoint.getName(), acupoint.getAcupointCode());
-        // 新增穴位后,异步为所有已生成穴位表的子用户追加该穴位坐标
-        try {
-            userAcupointService.generateForAllUsersByAcupoint(acupoint.getId());
-        } catch (Exception e) {
-            LogUtil.error(AcupointServiceImpl.class,
-                    "新增穴位[" + acupoint.getId() + "]触发用户坐标批量追加失败", e);
-        }
-    }
-
-    /**
-     * 修改穴位信息,校验穴位存在性及名称/编码唯一性
-     *
-     * @param acupoint 穴位信息
-     */
-    @Override
-    public void updateAcupoint(Acupoint acupoint) {
-        Acupoint exist = getById(acupoint.getId());
-        if (exist == null) {
-            throw new BusinessException(ResultCode.ACUPOINT_NOT_FOUND);
-        }
-        normalizeLocationFields(acupoint);
-        // 如果修改了编码,校验唯一性
-        if (StringUtils.hasText(acupoint.getAcupointCode())
-                && !acupoint.getAcupointCode().equals(exist.getAcupointCode())) {
-            long count = lambdaQuery().eq(Acupoint::getAcupointCode, acupoint.getAcupointCode()).count();
-            if (count > 0) {
-                throw new BusinessException(ResultCode.ACUPOINT_CODE_EXISTS);
-            }
-        }
-        // 如果修改了名称,校验唯一性
-        if (StringUtils.hasText(acupoint.getName())
-                && !acupoint.getName().equals(exist.getName())) {
-            long count = lambdaQuery().eq(Acupoint::getName, acupoint.getName()).count();
-            if (count > 0) {
-                throw new BusinessException(ResultCode.ACUPOINT_NAME_EXISTS);
-            }
-        }
-        updateById(acupoint);
-        LogUtil.info(AcupointServiceImpl.class, "修改穴位[{}]", acupoint.getId());
-        try {
-            userAcupointService.generateForAllUsersByAcupoint(acupoint.getId());
-        } catch (Exception e) {
-            LogUtil.error(AcupointServiceImpl.class,
-                    "修改穴位[" + acupoint.getId() + "]触发用户坐标批量更新失败", e);
-        }
-    }
-
-    /**
-     * 删除穴位(逻辑删除)
-     *
-     * @param id 穴位ID
-     */
-    @Override
-    public void deleteAcupoint(Long id) {
-        Acupoint acupoint = getById(id);
-        if (acupoint == null) {
-            throw new BusinessException(ResultCode.ACUPOINT_NOT_FOUND);
-        }
-        removeById(id);
-        LogUtil.info(AcupointServiceImpl.class, "删除穴位[{}]", id);
-    }
-
-    /**
-     * 批量删除穴位(逻辑删除)
-     *
-     * @param ids 穴位ID列表
-     */
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public void batchDeleteAcupoint(List<Long> ids) {
-        if (ids == null || ids.isEmpty()) {
-            throw new BusinessException(ResultCode.PARAM_ERROR);
-        }
-        removeByIds(ids);
-        LogUtil.info(AcupointServiceImpl.class, "批量删除穴位,共[{}]条", ids.size());
-    }
-
-    /**
-     * 批量导入穴位(跳过已存在编码)
-     *
-     * @param list 穴位信息列表
-     */
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public void batchImport(List<Acupoint> list) {
-        if (list == null || list.isEmpty()) {
-            return;
-        }
-        for (Acupoint acupoint : list) {
-            if (!StringUtils.hasText(acupoint.getAcupointCode())) {
-                continue;
-            }
-            normalizeLocationFields(acupoint);
-            Acupoint exist = lambdaQuery()
-                    .eq(Acupoint::getAcupointCode, acupoint.getAcupointCode())
-                    .one();
-            if (exist == null) {
-                save(acupoint);
-            } else {
-                acupoint.setId(exist.getId());
-                updateById(acupoint);
-            }
-        }
-        LogUtil.info(AcupointServiceImpl.class, "批量导入穴位[{}]条", list.size());
-    }
-
-    /**
-     * 统一从穴位定位描述中解析相对坐标参数,避免管理员直接维护算法字段。
-     */
-    private void normalizeLocationFields(Acupoint acupoint) {
-        if (acupoint == null) {
-            return;
-        }
-
-        AcupointLocationParser.ParseResult parsed = AcupointLocationParser.parse(acupoint);
-        acupoint.setLocationType(1);
-        acupoint.setYReferenceTerm(parsed.getYReferenceTerm());
-        acupoint.setNormalizedY(parsed.getNormalizedY());
-        acupoint.setLongitudeOffset(parsed.getLongitudeOffset());
-        acupoint.setLateralOffset(parsed.getLateralOffset());
-        acupoint.setSide(parsed.getSide());
-        acupoint.setParseStatus(parsed.getParseStatus());
-        acupoint.setParseConfidence(parsed.getParseConfidence());
-        acupoint.setParseMessage(parsed.getParseMessage());
-        acupoint.setFingerWidthType(null);
-
-        if (acupoint.getGender() == null) {
-            acupoint.setGender(0);
-        }
-        acupoint.setStatus(AcupointLocationParser.STATUS_PARSED.equals(parsed.getParseStatus()) ? 1 : 0);
-        if (acupoint.getDeleted() == null) {
-            acupoint.setDeleted(0);
-        }
-        if (acupoint.getProductAcupoint() == null) {
-            acupoint.setProductAcupoint(0);
-        }
-    }
-}

+ 0 - 236
code/backend/src/main/java/com/aijiuyi/admin/service/impl/AdminServiceImpl.java

@@ -1,236 +0,0 @@
-package com.aijiuyi.admin.service.impl;
-
-import com.aijiuyi.admin.common.constant.ResultCode;
-import com.aijiuyi.admin.common.context.RequestContext;
-import com.aijiuyi.admin.common.exception.BusinessException;
-import com.aijiuyi.admin.common.util.LogUtil;
-import com.aijiuyi.admin.controller.dto.AdminQueryDTO;
-import com.aijiuyi.admin.entity.AdminPermission;
-import com.aijiuyi.admin.entity.AppUser;
-import com.aijiuyi.admin.mapper.AdminPermissionMapper;
-import com.aijiuyi.admin.mapper.AppUserMapper;
-import com.aijiuyi.admin.service.AdminService;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.util.DigestUtils;
-import org.springframework.util.StringUtils;
-
-import java.util.List;
-
-/**
- * 管理员管理 Service 实现类
- * 操作 app_user 表中 userType=3 的管理员记录
- * 同时维护 admin_permission 表中的路由权限配置
- */
-@Service
-public class AdminServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements AdminService {
-
-    @Autowired
-    private AdminPermissionMapper adminPermissionMapper;
-
-    /**
-     * 分页查询管理员列表(仅 userType=3)
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @Override
-    public IPage<AppUser> pageList(AdminQueryDTO queryDTO) {
-        Page<AppUser> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
-        LambdaQueryWrapper<AppUser> wrapper = new LambdaQueryWrapper<AppUser>()
-                .eq(AppUser::getUserType, 3)
-                .eq(queryDTO.getAdminRole() != null, AppUser::getAdminRole, queryDTO.getAdminRole())
-                .eq(queryDTO.getStatus() != null, AppUser::getStatus, queryDTO.getStatus())
-                .orderByAsc(AppUser::getId);
-        // 用户名/姓名 OR 模糊查询
-        boolean hasKeyword = StringUtils.hasText(queryDTO.getUsername()) || StringUtils.hasText(queryDTO.getNickname());
-        if (hasKeyword) {
-            String kw = StringUtils.hasText(queryDTO.getUsername()) ? queryDTO.getUsername() : queryDTO.getNickname();
-            wrapper.and(w -> w.like(AppUser::getUsername, kw).or().like(AppUser::getNickname, kw));
-        }
-        return page(page, wrapper);
-    }
-
-    /**
-     * 新增管理员
-     * 校验用户名唯一性,对密码进行 MD5 加密,userType 强制设为 3
-     *
-     * @param admin 管理员信息
-     */
-    @Override
-    public void addAdmin(AppUser admin) {
-        // 校验用户名唯一性
-        if (!StringUtils.hasText(admin.getUsername())) {
-            throw new BusinessException(ResultCode.PARAM_ERROR);
-        }
-        long usernameCount = lambdaQuery().eq(AppUser::getUsername, admin.getUsername()).count();
-        if (usernameCount > 0) {
-            throw new BusinessException(ResultCode.USERNAME_EXISTS);
-        }
-        // 强制 userType=3(管理员)
-        admin.setUserType(3);
-        // 密码加密
-        if (StringUtils.hasText(admin.getPassword())) {
-            admin.setPassword(DigestUtils.md5DigestAsHex(admin.getPassword().getBytes()));
-        }
-        save(admin);
-        LogUtil.info(AdminServiceImpl.class, "新增管理员[{}],角色[{}]", admin.getUsername(), admin.getAdminRole());
-    }
-
-    /**
-     * 修改管理员信息
-     * 若传入密码则重新加密,否则保留原密码;不允许修改 userType
-     *
-     * @param admin 管理员信息(需携带 id)
-     */
-    @Override
-    public void updateAdmin(AppUser admin) {
-        AppUser exist = getById(admin.getId());
-        if (exist == null || !Integer.valueOf(3).equals(exist.getUserType())) {
-            throw new BusinessException(ResultCode.ADMIN_NOT_FOUND);
-        }
-        // 若修改了用户名,校验唯一性
-        if (StringUtils.hasText(admin.getUsername()) && !admin.getUsername().equals(exist.getUsername())) {
-            long count = lambdaQuery().eq(AppUser::getUsername, admin.getUsername()).count();
-            if (count > 0) {
-                throw new BusinessException(ResultCode.USERNAME_EXISTS);
-            }
-        }
-        // 密码处理
-        if (StringUtils.hasText(admin.getPassword())) {
-            admin.setPassword(DigestUtils.md5DigestAsHex(admin.getPassword().getBytes()));
-        } else {
-            admin.setPassword(null);
-        }
-        // 强制 userType 不变
-        admin.setUserType(null);
-        updateById(admin);
-        LogUtil.info(AdminServiceImpl.class, "修改管理员[{}]", admin.getId());
-    }
-
-    /**
-     * 删除管理员(逻辑删除)
-     * adminRole=1 超级管理员不可删除
-     *
-     * @param id 管理员ID
-     */
-    @Override
-    public void deleteAdmin(Long id) {
-        AppUser admin = getById(id);
-        if (admin == null || !Integer.valueOf(3).equals(admin.getUserType())) {
-            throw new BusinessException(ResultCode.ADMIN_NOT_FOUND);
-        }
-        if (Integer.valueOf(1).equals(admin.getAdminRole())) {
-            throw new BusinessException(ResultCode.ADMIN_CANNOT_DELETE);
-        }
-        removeById(id);
-        LogUtil.info(AdminServiceImpl.class, "删除管理员[{}]", id);
-    }
-
-    /**
-     * 批量删除管理员(逻辑删除)
-     * 超级管理员(adminRole=1)不可被批量删除
-     *
-     * @param ids 管理员ID列表
-     */
-    @Override
-    public void batchDeleteAdmin(List<Long> ids) {
-        for (Long id : ids) {
-            AppUser admin = getById(id);
-            if (admin != null && Integer.valueOf(1).equals(admin.getAdminRole())) {
-                throw new BusinessException(ResultCode.ADMIN_CANNOT_DELETE);
-            }
-        }
-        // 仅删除 userType=3 的记录,防止误删其他类型用户
-        LambdaQueryWrapper<AppUser> wrapper = new LambdaQueryWrapper<AppUser>()
-                .in(AppUser::getId, ids)
-                .eq(AppUser::getUserType, 3);
-        remove(wrapper);
-        LogUtil.info(AdminServiceImpl.class, "批量删除管理员,ID列表:{}", ids);
-    }
-
-    /**
-     * 修改管理员状态(禁用/启用)
-     * 不允许禁用当前登录账号自身
-     *
-     * @param id     管理员ID
-     * @param status 目标状态:1=启用,0=禁用
-     */
-    @Override
-    public void updateStatus(Long id, Integer status) {
-        // 不允许禁用自身
-        Long currentUserId = RequestContext.getUserId();
-        if (Integer.valueOf(0).equals(status) && id.equals(currentUserId)) {
-            throw new BusinessException(ResultCode.ADMIN_CANNOT_DISABLE_SELF);
-        }
-        AppUser admin = getById(id);
-        if (admin == null || !Integer.valueOf(3).equals(admin.getUserType())) {
-            throw new BusinessException(ResultCode.ADMIN_NOT_FOUND);
-        }
-        AppUser update = new AppUser();
-        update.setId(id);
-        update.setStatus(status);
-        updateById(update);
-        LogUtil.info(AdminServiceImpl.class, "管理员[{}]状态修改为[{}]", id, status);
-    }
-
-    /**
-     * 查询管理员权限路由列表
-     *
-     * @param adminId 管理员ID
-     * @return 路由路径 JSON 数组字符串;超级管理员返回 null
-     */
-    @Override
-    public String getPermissions(Long adminId) {
-        AppUser admin = getById(adminId);
-        if (admin == null) {
-            throw new BusinessException(ResultCode.ADMIN_NOT_FOUND);
-        }
-        // 超级管理员返回 null,前端视为全部权限
-        if (Integer.valueOf(1).equals(admin.getAdminRole())) {
-            return null;
-        }
-        AdminPermission permission = adminPermissionMapper.selectOne(
-                new LambdaQueryWrapper<AdminPermission>()
-                        .eq(AdminPermission::getAdminId, adminId)
-        );
-        return permission != null ? permission.getRoutePaths() : "[]";
-    }
-
-    /**
-     * 更新管理员权限路由列表
-     * 超级管理员(adminRole=1)调用时直接忽略,不修改
-     *
-     * @param adminId    管理员ID
-     * @param routePaths 路由路径 JSON 数组字符串
-     */
-    @Override
-    public void updatePermissions(Long adminId, String routePaths) {
-        AppUser admin = getById(adminId);
-        if (admin == null || !Integer.valueOf(3).equals(admin.getUserType())) {
-            throw new BusinessException(ResultCode.ADMIN_NOT_FOUND);
-        }
-        // 超级管理员不设置权限
-        if (Integer.valueOf(1).equals(admin.getAdminRole())) {
-            return;
-        }
-        AdminPermission exist = adminPermissionMapper.selectOne(
-                new LambdaQueryWrapper<AdminPermission>()
-                        .eq(AdminPermission::getAdminId, adminId)
-        );
-        if (exist == null) {
-            AdminPermission permission = new AdminPermission();
-            permission.setAdminId(adminId);
-            permission.setRoutePaths(routePaths);
-            adminPermissionMapper.insert(permission);
-        } else {
-            exist.setRoutePaths(routePaths);
-            adminPermissionMapper.updateById(exist);
-        }
-        LogUtil.info(AdminServiceImpl.class, "更新管理员[{}]权限:{}", adminId, routePaths);
-    }
-}

+ 0 - 272
code/backend/src/main/java/com/aijiuyi/admin/service/impl/AppUserServiceImpl.java

@@ -1,272 +0,0 @@
-package com.aijiuyi.admin.service.impl;
-
-import com.aijiuyi.admin.common.constant.ResultCode;
-import com.aijiuyi.admin.common.context.RequestContext;
-import com.aijiuyi.admin.common.entity.Result;
-import com.aijiuyi.admin.common.exception.BusinessException;
-import com.aijiuyi.admin.common.interceptor.TokenInterceptor;
-import com.aijiuyi.admin.common.util.LogUtil;
-import com.aijiuyi.admin.common.util.RedisUtil;
-import com.aijiuyi.admin.common.util.TokenUtil;
-import com.aijiuyi.admin.controller.dto.AppUserQueryDTO;
-import com.aijiuyi.admin.controller.dto.LoginDTO;
-import com.aijiuyi.admin.entity.AdminPermission;
-import com.aijiuyi.admin.entity.AppUser;
-import com.aijiuyi.admin.mapper.AdminPermissionMapper;
-import com.aijiuyi.admin.mapper.AppUserMapper;
-import com.aijiuyi.admin.service.AppUserService;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.util.DigestUtils;
-import org.springframework.util.StringUtils;
-
-import java.time.LocalDateTime;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 应用用户 Service 实现类
- * 统一用户中心:普通用户、专家、管理员均使用此表
- */
-@Service
-public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements AppUserService {
-
-    /** Token 在 Redis 中的 key 前缀 */
-    private static final String TOKEN_REDIS_PREFIX = TokenInterceptor.REDIS_TOKEN_KEY_PREFIX;
-
-    /** Token 在 Redis 中的过期时间:3年(秒) */
-    private static final long TOKEN_EXPIRE_SECONDS = 3L * 365 * 24 * 60 * 60;
-
-    @Autowired
-    private RedisUtil redisUtil;
-
-    @Autowired
-    private AdminPermissionMapper adminPermissionMapper;
-
-    /**
-     * 管理员登录
-     * 通过用户名+密码校验身份,生成 JWT Token 并存入 Redis
-     *
-     * @param loginDTO 登录参数
-     * @return 包含 token 和用户基本信息的 Result
-     */
-    @Override
-    public Result<Map<String, Object>> login(LoginDTO loginDTO) {
-        LogUtil.info(AppUserServiceImpl.class, "用户[{}]尝试登录", loginDTO.getUsername());
-
-        // 根据用户名查询用户(仅管理员可通过用户名登录)
-        AppUser user = lambdaQuery()
-                .eq(AppUser::getUsername, loginDTO.getUsername())
-                .one();
-
-        // 用户不存在
-        if (user == null) {
-            LogUtil.warn(AppUserServiceImpl.class, "用户[{}]不存在", loginDTO.getUsername());
-            throw new BusinessException(ResultCode.USER_NOT_FOUND);
-        }
-
-        // 验证密码(前端传明文,后端 MD5 加密后比对)
-        String md5Password = DigestUtils.md5DigestAsHex(loginDTO.getPassword().getBytes());
-        if (!md5Password.equals(user.getPassword())) {
-            LogUtil.warn(AppUserServiceImpl.class, "用户[{}]密码错误", loginDTO.getUsername());
-            throw new BusinessException(ResultCode.USER_PASSWORD_ERROR);
-        }
-
-        // 验证用户状态
-        if (user.getStatus() != null && user.getStatus() == 0) {
-            LogUtil.warn(AppUserServiceImpl.class, "用户[{}]已被禁用", loginDTO.getUsername());
-            throw new BusinessException(ResultCode.USER_DISABLED);
-        }
-
-        // 验证角色(仅 userType=3=管理员 可登录后台)
-        if (user.getUserType() == null || user.getUserType() != 3) {
-            LogUtil.warn(AppUserServiceImpl.class, "用户[{}]无后台登录权限", loginDTO.getUsername());
-            throw new BusinessException(ResultCode.FORBIDDEN);
-        }
-
-        // 生成 JWT Token(携带 userType)
-        String token = TokenUtil.generateToken(user.getId(), user.getUsername(), user.getUserType());
-
-        // 将 Token 存入 Redis,3年过期
-        redisUtil.set(TOKEN_REDIS_PREFIX + token, user.getId(), TOKEN_EXPIRE_SECONDS);
-
-        // 更新最后登录时间
-        AppUser updateLogin = new AppUser();
-        updateLogin.setId(user.getId());
-        updateLogin.setLastLoginTime(LocalDateTime.now());
-        updateById(updateLogin);
-
-        // 登录成功后将用户信息写入 RequestContext,供 LogAspect 正确记录操作人
-        RequestContext.setUserId(user.getId());
-        RequestContext.setUserName(user.getUsername());
-        RequestContext.setUserType(user.getUserType());
-
-        LogUtil.info(AppUserServiceImpl.class, "用户[{}]登录成功", user.getUsername());
-
-        // 查询权限路由(超级管理员返回 null,代表全部权限)
-        String routePaths = null;
-        if (!Integer.valueOf(1).equals(user.getAdminRole())) {
-            AdminPermission permission = adminPermissionMapper.selectOne(
-                    new LambdaQueryWrapper<AdminPermission>()
-                            .eq(AdminPermission::getAdminId, user.getId())
-            );
-            routePaths = permission != null ? permission.getRoutePaths() : "[]";
-        }
-
-        // 组装返回数据
-        Map<String, Object> resultData = new HashMap<>(8);
-        resultData.put("token", token);
-        resultData.put("userId", user.getId());
-        resultData.put("username", user.getUsername());
-        resultData.put("nickname", user.getNickname());
-        resultData.put("avatar", user.getAvatar());
-        resultData.put("userType", user.getUserType());
-        resultData.put("adminRole", user.getAdminRole());
-        resultData.put("permissions", routePaths);
-
-        return Result.success(resultData);
-    }
-
-    /**
-     * 退出登录
-     * 从 Redis 中删除 Token,使 Token 立即失效
-     *
-     * @param token 当前用户的 Token
-     */
-    @Override
-    public void logout(String token) {
-        if (StringUtils.hasText(token)) {
-            // 去掉 Bearer 前缀
-            if (token.startsWith("Bearer ")) {
-                token = token.substring(7);
-            }
-            // 从 Redis 删除 Token
-            redisUtil.del(TOKEN_REDIS_PREFIX + token);
-            LogUtil.info(AppUserServiceImpl.class, "用户退出登录,Token 已失效");
-        }
-    }
-
-    /**
-     * 分页查询应用用户列表
-     *
-     * @param queryDTO 查询条件
-     * @return 分页结果
-     */
-    @Override
-    public IPage<AppUser> pageList(AppUserQueryDTO queryDTO) {
-        Page<AppUser> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
-        LambdaQueryWrapper<AppUser> wrapper = new LambdaQueryWrapper<>();
-        wrapper.like(StringUtils.hasText(queryDTO.getPhone()), AppUser::getPhone, queryDTO.getPhone())
-               .like(StringUtils.hasText(queryDTO.getNickname()), AppUser::getNickname, queryDTO.getNickname())
-               .eq(queryDTO.getUserType() != null, AppUser::getUserType, queryDTO.getUserType())
-               .eq(queryDTO.getStatus() != null, AppUser::getStatus, queryDTO.getStatus())
-               .orderByDesc(AppUser::getCreateTime);
-        return page(page, wrapper);
-    }
-
-    /**
-     * 修改用户状态(禁用/启用)
-     *
-     * @param id     用户ID
-     * @param status 目标状态:1=启用,0=禁用
-     */
-    @Override
-    public void updateStatus(Long id, Integer status) {
-        AppUser user = getById(id);
-        if (user == null) {
-            throw new BusinessException(ResultCode.APP_USER_NOT_FOUND);
-        }
-        AppUser update = new AppUser();
-        update.setId(id);
-        update.setStatus(status);
-        updateById(update);
-        LogUtil.info(AppUserServiceImpl.class, "应用用户[{}]状态修改为[{}]", id, status);
-    }
-
-    /**
-     * 新增应用用户
-     * 若为管理员(userType=3)则校验用户名唯一性并对密码做 MD5 加密
-     *
-     * @param appUser 用户信息
-     */
-    @Override
-    public void addUser(AppUser appUser) {
-        // 手机号唯一性校验(手机号不为空时校验)
-        if (StringUtils.hasText(appUser.getPhone())) {
-            long phoneCount = lambdaQuery().eq(AppUser::getPhone, appUser.getPhone()).count();
-            if (phoneCount > 0) {
-                throw new BusinessException(ResultCode.PHONE_EXISTS);
-            }
-        }
-        // 管理员需校验用户名唯一性,并对密码加密
-        if (Integer.valueOf(3).equals(appUser.getUserType())) {
-            if (StringUtils.hasText(appUser.getUsername())) {
-                long usernameCount = lambdaQuery().eq(AppUser::getUsername, appUser.getUsername()).count();
-                if (usernameCount > 0) {
-                    throw new BusinessException(ResultCode.USERNAME_EXISTS);
-                }
-            }
-            // 密码 MD5 加密
-            if (StringUtils.hasText(appUser.getPassword())) {
-                appUser.setPassword(DigestUtils.md5DigestAsHex(appUser.getPassword().getBytes()));
-            }
-        }
-        save(appUser);
-        LogUtil.info(AppUserServiceImpl.class, "新增应用用户,手机号[{}],类型[{}]", appUser.getPhone(), appUser.getUserType());
-    }
-
-    /**
-     * 修改应用用户
-     *
-     * @param appUser 用户信息
-     */
-    @Override
-    public void updateUser(AppUser appUser) {
-        AppUser exist = getById(appUser.getId());
-        if (exist == null) {
-            throw new BusinessException(ResultCode.APP_USER_NOT_FOUND);
-        }
-        // 如果修改了手机号,校验唯一性
-        if (StringUtils.hasText(appUser.getPhone()) && !appUser.getPhone().equals(exist.getPhone())) {
-            long count = lambdaQuery().eq(AppUser::getPhone, appUser.getPhone()).count();
-            if (count > 0) {
-                throw new BusinessException(ResultCode.PHONE_EXISTS);
-            }
-        }
-        // 如果修改了用户名,校验唯一性
-        if (StringUtils.hasText(appUser.getUsername()) && !appUser.getUsername().equals(exist.getUsername())) {
-            long count = lambdaQuery().eq(AppUser::getUsername, appUser.getUsername()).count();
-            if (count > 0) {
-                throw new BusinessException(ResultCode.USERNAME_EXISTS);
-            }
-        }
-        // 如果修改了密码,重新加密
-        if (StringUtils.hasText(appUser.getPassword())) {
-            appUser.setPassword(DigestUtils.md5DigestAsHex(appUser.getPassword().getBytes()));
-        } else {
-            // 不修改密码字段
-            appUser.setPassword(null);
-        }
-        updateById(appUser);
-        LogUtil.info(AppUserServiceImpl.class, "修改应用用户[{}]", appUser.getId());
-    }
-
-    /**
-     * 删除应用用户(逻辑删除)
-     *
-     * @param id 用户ID
-     */
-    @Override
-    public void deleteUser(Long id) {
-        AppUser user = getById(id);
-        if (user == null) {
-            throw new BusinessException(ResultCode.APP_USER_NOT_FOUND);
-        }
-        removeById(id);
-        LogUtil.info(AppUserServiceImpl.class, "删除应用用户[{}]", id);
-    }
-}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません