Compare commits

..

2 Commits

Author SHA1 Message Date
hewendi23 7c2f2c21e8 1
4 months ago
hewendi23 7b39e636bd 1
4 months ago

@ -1,210 +0,0 @@
# 个人周总结-第15周文档编写人员
**姓  名:** 刘兴朋
**团队名称:** 菜鸟队
**开始时间:** 2025-12-29
**结束时间:** 2026-01-04
## 📊 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
|------|----------|----------|----------|
| 1 | β版本测试报告质量审查与完善 | 完成 | 完成了《β版本测试分析报告》的全面审查提出了28条修改建议并全部落实补充了测试方法论和质量评估体系。 |
| 2 | 用户手册全流程验证与优化 | 完成 | 进行了3轮全流程走测发现了12处文档与实际功能不符的问题优化了图文说明提升了用户体验友好度。 |
| 3 | 部署指南完整性与可操作性验证 | 完成 | 验证了Docker-Compose部署指南的每个步骤新成员平均部署时间控制在5分钟内成功率达到100%。 |
| 4 | 项目结项汇报PPT技术支持 | 完成 | 协助整理了技术架构图、功能亮点、性能数据等内容技术表述准确率100%,获得团队高度认可。 |
| 5 | 系统演示视频脚本审核与优化 | 完成 | 审核并优化了演示视频脚本优化了技术展示逻辑演示视频时长8分钟覆盖所有核心功能。 |
| 6 | β版本发布包文档整合 | 完成 | 整合了6大类28个文档建立了完整的发布包目录结构编写了详细的发布说明文档。 |
| 7 | 代码文档一致性最终核查 | 完成 | 完成了文档与代码的最终一致性核查关键功能描述准确率100%次要描述不一致率0.5%。 |
| 8 | 项目知识库与经验总结文档 | 完成 | 整理了项目全过程文档,建立了完整的项目知识库,编写了《项目经验总结报告》初稿。 |
## 📁 交付成果清单
### 测试报告文档(实际交付)
- ✅ 《β版本测试分析报告最终版35页
- 功能测试结果覆盖8大模块56个功能点通过率98.2%
- 性能测试数据核心接口平均响应时间128ms并发支持100用户
- 遗留Bug评估3个低优先级Bug均已制定规避方案
- 质量评估体系建立5个维度质量评分模型
- ✅ 《测试报告质量审查意见书》记录28条改进建议及落实情况
### 用户与部署文档(实际交付)
- ✅ 《用户操作手册图文最终版42页
- 全流程走测验证记录发现并修复12处问题
- 用户友好性优化:增加了操作提示和常见错误预防
- 多终端适配说明:覆盖桌面、平板、手机三种设备
- ✅ 《Docker-Compose一键部署指南验证版18页
- 部署步骤验证新成员平均部署时间5分钟
- 常见问题解决方案库收录15个常见问题及解决方案
- 环境要求说明:明确了硬件、软件、网络要求
### 演示与汇报材料(实际交付)
- ✅ 项目结项汇报PPT技术内容审核报告提出12处优化建议
- ✅ 系统演示视频脚本优化方案时长优化至8分钟逻辑更清晰
- ✅ 技术亮点与创新点汇总文档总结8大技术亮点
### 最终交付包(实际交付)
- ✅ β版本完整发布包总大小256MB包含6大类28个文档
- ✅ 发布说明文档(详细说明版本特性、系统要求、部署步骤)
- ✅ 项目知识库结构与索引建立5大类知识分类体系
- ✅ 项目经验总结报告初稿(总结项目全过程经验教训)
- ✅ 第15周文档交付工作总结全面总结文档工作成效
## 🏆 工作亮点与成效
### 全流程走测验证成效显著
1. **问题发现能力**通过3轮走测发现12处文档与实际不符问题
2. **用户视角优化**:从新手用户角度优化文档,提升用户体验
3. **验证流程标准化**:建立了标准化的文档验证流程和检查清单
### 文档质量保障体系完善
1. **三层验证机制有效**:准确性、可操作性、用户体验验证全部达标
2. **一致性核查精准**关键功能描述准确率100%,为历史最高水平
3. **风险管理到位**:识别并应对了所有文档相关风险
### 量化成果突出
| 指标 | 目标值 | 实际值 | 达成率 |
|------|--------|--------|--------|
| 文档准确性 | 100% | 100% | 100% |
| 部署成功率 | ≥95% | 100% | 105% |
| 走测问题发现数 | ≥5个 | 12个 | 240% |
| 团队文档满意度 | ≥4.5分 | 4.9分 | 109% |
| 文档交付及时率 | 100% | 100% | 100% |
## 🔍 关键问题与深度解决方案
### 文档与实际功能不符问题
**问题发现**在全流程走测中发现了12处文档与实际功能不符的问题主要类型
1. 操作步骤顺序错误3处
2. 界面元素描述不准确5处
3. 数据格式说明有误2处
4. 异常处理描述缺失2处
**深度解决方案**
1. **建立文档验证矩阵**
| 验证维度 | 验证方法 | 验证标准 |
|----------|----------|----------|
| 步骤准确性 | 逐步骤执行 | 100%匹配 |
| 界面一致性 | 截图比对 | 像素级匹配 |
| 数据格式 | 数据验证 | 格式完全一致 |
| 异常处理 | 异常触发 | 描述完整准确 |
2. **实施三重验证机制**
- 开发人员自验:代码提交时同步更新文档
- 文档人员验证:全流程走测验证
- 新手用户验证:邀请外部人员试用反馈
3. **建立文档更新触发器**
- 代码变更时自动提示文档更新
- 建立文档版本与代码版本关联
- 定期进行文档健康度检查
### 部署环境适配性问题
**问题发现**在验证部署指南时发现2处环境适配性问题
1. 特定操作系统依赖包缺失
2. 网络代理环境下的镜像拉取失败
**解决方案实施**
1. **环境检查脚本开发**
```bash
# 环境预检查脚本
check_environment() {
# 检查操作系统
# 检查Docker版本
# 检查端口占用
# 检查网络连接
}
```
2. **多环境适配方案**
- 提供Windows/macOS/Linux三平台部署指南
- 针对企业网络环境提供代理配置方案
- 建立离线部署包和在线部署双方案
3. **故障诊断工具集成**
- 部署过程中的实时状态监控
- 错误日志自动收集和分析
- 一键故障诊断和修复
## 📈 工作方法创新与实践
### 文档验证方法创新
1. **自动化验证工具开发**:开发了文档链接检查和版本对比工具
2. **用户视角测试法**:邀请完全不了解项目的人员进行文档测试
3. **数据驱动优化法**:收集文档使用数据,针对性优化高频问题
### 质量保障体系升级
1. **文档质量评分体系**建立了5维度文档质量评分模型
2. **问题预防机制**:通过走测发现潜在问题,提前预防
3. **持续改进流程**:建立了文档质量的持续改进循环
### 团队协作效率提升
1. **文档协作平台优化**:优化了文档协作流程和工具
2. **知识共享机制**:建立了文档经验共享和最佳实践库
3. **培训体系完善**:为新成员建立了文档使用和贡献指南
## 📌 本周小结
作为文档编写人员,本周在"β版本交付与结项"工作中圆满完成了所有文档任务,建立了完整、专业、高质量的文档交付体系,为项目的完美收官提供了坚实的文档保障。
### 核心价值体现:
1. **质量守护者**确保了所有交付文档的100%准确性
2. **用户体验设计师**:从用户角度优化文档,提升使用体验
3. **知识体系构建者**:建立了完整的项目知识体系和文档架构
4. **团队协作促进者**:通过文档工作促进了团队的高效协作
### 关键成就总结:
- 建立了β版本完整的文档交付体系
- 实现了文档与实际功能的100%一致性
- 优化了用户体验部署成功率达到100%
- 建立了项目知识库和经验总结体系
- 获得团队成员4.9/5的高度评价
## 🧠 项目经验深度总结
### 文档工作的成功经验
1. **系统性思维**:从项目开始就规划完整的文档体系
2. **用户为中心**:始终从用户角度思考和优化文档
3. **质量优先**:将文档质量放在首位,建立严格的质量标准
4. **持续改进**:不断收集反馈,持续优化文档
### 技术文档管理的核心原则
1. **一致性原则**:确保文档与代码、文档之间的一致性
2. **实用性原则**:文档必须实用,能够指导实际工作
3. **可维护性原则**:文档结构清晰,易于维护和更新
4. **可检索性原则**:建立高效的文档检索机制
### 团队协作的关键要素
1. **沟通及时性**:技术变更及时同步到文档
2. **责任明确性**:明确文档工作的责任和流程
3. **工具标准化**:使用标准化的文档工具和流程
4. **知识共享文化**:建立知识共享和学习的团队文化
## 📊 项目知识库建设成果
### 知识库结构完善
1. **分类体系科学**5大类25个子类的知识分类体系
2. **检索机制高效**:关键词检索、分类浏览、关联推荐
3. **权限管理合理**:根据角色设置不同的访问和编辑权限
### 知识沉淀丰富
1. **技术文档完整**:覆盖需求、设计、开发、测试、部署全流程
2. **经验教训系统**:系统化整理了项目经验和教训
3. **最佳实践丰富**:总结了各个技术领域的最佳实践
### 知识复用有效
1. **学习路径清晰**:为新成员设计了清晰的学习路径
2. **问题解决高效**:常见问题都有现成的解决方案
3. **经验传承有序**:建立了有序的知识传承机制
## 🎯 个人成长与收获
### 专业技能提升
1. **技术文档编写能力**:掌握了复杂技术文档的编写方法
2. **质量保障能力**:建立了完整的文档质量保障体系
3. **项目管理能力**:提升了项目文档的整体规划和管理能力
### 团队协作能力
1. **沟通协调能力**:提升了与开发、测试、产品等多角色协作能力
2. **知识管理能力**:掌握了项目知识管理和传承的方法
3. **问题解决能力**:提升了文档相关问题的分析和解决能力

@ -1,86 +0,0 @@
# Details
Date : 2026-01-05 15:04:31
Directory c:\\Users\\87641\\Downloads\\simple-alipay-backend\\src
Total : 71 files, 3342 codes, 42 comments, 654 blanks, all 4038 lines
[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
## Files
| filename | language | code | comment | blank | total |
| :--- | :--- | ---: | ---: | ---: | ---: |
| [src/main/java/com/example/alipay/SimpleAlipayBackendApplication.java](/src/main/java/com/example/alipay/SimpleAlipayBackendApplication.java) | Java | 9 | 0 | 3 | 12 |
| [src/main/java/com/example/alipay/config/DataLoader.java](/src/main/java/com/example/alipay/config/DataLoader.java) | Java | 51 | 0 | 5 | 56 |
| [src/main/java/com/example/alipay/config/GlobalExceptionHandler.java](/src/main/java/com/example/alipay/config/GlobalExceptionHandler.java) | Java | 83 | 0 | 16 | 99 |
| [src/main/java/com/example/alipay/config/fintech/SecurityConfig.java](/src/main/java/com/example/alipay/config/fintech/SecurityConfig.java) | Java | 77 | 5 | 9 | 91 |
| [src/main/java/com/example/alipay/controller/CollectionController.java](/src/main/java/com/example/alipay/controller/CollectionController.java) | Java | 40 | 0 | 7 | 47 |
| [src/main/java/com/example/alipay/controller/PaymentController.java](/src/main/java/com/example/alipay/controller/PaymentController.java) | Java | 68 | 0 | 8 | 76 |
| [src/main/java/com/example/alipay/controller/SmartAssistantController.java](/src/main/java/com/example/alipay/controller/SmartAssistantController.java) | Java | 29 | 0 | 9 | 38 |
| [src/main/java/com/example/alipay/controller/TravelController.java](/src/main/java/com/example/alipay/controller/TravelController.java) | Java | 41 | 0 | 8 | 49 |
| [src/main/java/com/example/alipay/controller/admin/AdminAuthController.java](/src/main/java/com/example/alipay/controller/admin/AdminAuthController.java) | Java | 81 | 0 | 15 | 96 |
| [src/main/java/com/example/alipay/controller/admin/AdminLogController.java](/src/main/java/com/example/alipay/controller/admin/AdminLogController.java) | Java | 29 | 0 | 8 | 37 |
| [src/main/java/com/example/alipay/controller/admin/DiscountPolicyController.java](/src/main/java/com/example/alipay/controller/admin/DiscountPolicyController.java) | Java | 87 | 0 | 14 | 101 |
| [src/main/java/com/example/alipay/controller/admin/UserManagementController.java](/src/main/java/com/example/alipay/controller/admin/UserManagementController.java) | Java | 72 | 0 | 13 | 85 |
| [src/main/java/com/example/alipay/controller/fintech/AssetController.java](/src/main/java/com/example/alipay/controller/fintech/AssetController.java) | Java | 61 | 0 | 12 | 73 |
| [src/main/java/com/example/alipay/controller/fintech/AuthController.java](/src/main/java/com/example/alipay/controller/fintech/AuthController.java) | Java | 124 | 0 | 16 | 140 |
| [src/main/java/com/example/alipay/controller/fintech/BillController.java](/src/main/java/com/example/alipay/controller/fintech/BillController.java) | Java | 48 | 0 | 10 | 58 |
| [src/main/java/com/example/alipay/controller/site/GateEventController.java](/src/main/java/com/example/alipay/controller/site/GateEventController.java) | Java | 82 | 1 | 15 | 98 |
| [src/main/java/com/example/alipay/controller/site/StationManagementController.java](/src/main/java/com/example/alipay/controller/site/StationManagementController.java) | Java | 136 | 3 | 20 | 159 |
| [src/main/java/com/example/alipay/model/AssistantMessage.java](/src/main/java/com/example/alipay/model/AssistantMessage.java) | Java | 29 | 1 | 8 | 38 |
| [src/main/java/com/example/alipay/model/CollectionQRCode.java](/src/main/java/com/example/alipay/model/CollectionQRCode.java) | Java | 22 | 0 | 4 | 26 |
| [src/main/java/com/example/alipay/model/Payment.java](/src/main/java/com/example/alipay/model/Payment.java) | Java | 35 | 0 | 4 | 39 |
| [src/main/java/com/example/alipay/model/TravelPass.java](/src/main/java/com/example/alipay/model/TravelPass.java) | Java | 28 | 1 | 4 | 33 |
| [src/main/java/com/example/alipay/model/TravelRecord.java](/src/main/java/com/example/alipay/model/TravelRecord.java) | Java | 32 | 1 | 4 | 37 |
| [src/main/java/com/example/alipay/model/UserAccount.java](/src/main/java/com/example/alipay/model/UserAccount.java) | Java | 22 | 1 | 4 | 27 |
| [src/main/java/com/example/alipay/model/admin/AdminOperationLog.java](/src/main/java/com/example/alipay/model/admin/AdminOperationLog.java) | Java | 22 | 0 | 8 | 30 |
| [src/main/java/com/example/alipay/model/admin/AdminUser.java](/src/main/java/com/example/alipay/model/admin/AdminUser.java) | Java | 29 | 1 | 9 | 39 |
| [src/main/java/com/example/alipay/model/admin/CreateAdminRequest.java](/src/main/java/com/example/alipay/model/admin/CreateAdminRequest.java) | Java | 8 | 0 | 3 | 11 |
| [src/main/java/com/example/alipay/model/admin/DiscountPolicy.java](/src/main/java/com/example/alipay/model/admin/DiscountPolicy.java) | Java | 27 | 0 | 8 | 35 |
| [src/main/java/com/example/alipay/model/fintech/BankCard.java](/src/main/java/com/example/alipay/model/fintech/BankCard.java) | Java | 14 | 0 | 3 | 17 |
| [src/main/java/com/example/alipay/model/fintech/Bill.java](/src/main/java/com/example/alipay/model/fintech/Bill.java) | Java | 21 | 0 | 7 | 28 |
| [src/main/java/com/example/alipay/model/fintech/User.java](/src/main/java/com/example/alipay/model/fintech/User.java) | Java | 39 | 0 | 11 | 50 |
| [src/main/java/com/example/alipay/model/fintech/UserAudit.java](/src/main/java/com/example/alipay/model/fintech/UserAudit.java) | Java | 16 | 0 | 3 | 19 |
| [src/main/java/com/example/alipay/model/fintech/UserStatus.java](/src/main/java/com/example/alipay/model/fintech/UserStatus.java) | Java | 7 | 0 | 2 | 9 |
| [src/main/java/com/example/alipay/model/site/Gate.java](/src/main/java/com/example/alipay/model/site/Gate.java) | Java | 22 | 0 | 7 | 29 |
| [src/main/java/com/example/alipay/model/site/GateEvent.java](/src/main/java/com/example/alipay/model/site/GateEvent.java) | Java | 28 | 0 | 9 | 37 |
| [src/main/java/com/example/alipay/model/site/Station.java](/src/main/java/com/example/alipay/model/site/Station.java) | Java | 21 | 0 | 6 | 27 |
| [src/main/java/com/example/alipay/repository/AssistantMessageRepository.java](/src/main/java/com/example/alipay/repository/AssistantMessageRepository.java) | Java | 7 | 1 | 2 | 10 |
| [src/main/java/com/example/alipay/repository/CollectionQRCodeRepository.java](/src/main/java/com/example/alipay/repository/CollectionQRCodeRepository.java) | Java | 5 | 0 | 3 | 8 |
| [src/main/java/com/example/alipay/repository/PaymentRepository.java](/src/main/java/com/example/alipay/repository/PaymentRepository.java) | Java | 10 | 0 | 4 | 14 |
| [src/main/java/com/example/alipay/repository/TravelPassRepository.java](/src/main/java/com/example/alipay/repository/TravelPassRepository.java) | Java | 7 | 0 | 4 | 11 |
| [src/main/java/com/example/alipay/repository/TravelRecordRepository.java](/src/main/java/com/example/alipay/repository/TravelRecordRepository.java) | Java | 7 | 0 | 4 | 11 |
| [src/main/java/com/example/alipay/repository/UserAccountRepository.java](/src/main/java/com/example/alipay/repository/UserAccountRepository.java) | Java | 14 | 0 | 6 | 20 |
| [src/main/java/com/example/alipay/repository/admin/AdminOperationLogRepository.java](/src/main/java/com/example/alipay/repository/admin/AdminOperationLogRepository.java) | Java | 8 | 0 | 3 | 11 |
| [src/main/java/com/example/alipay/repository/admin/AdminUserRepository.java](/src/main/java/com/example/alipay/repository/admin/AdminUserRepository.java) | Java | 8 | 0 | 3 | 11 |
| [src/main/java/com/example/alipay/repository/admin/DiscountPolicyRepository.java](/src/main/java/com/example/alipay/repository/admin/DiscountPolicyRepository.java) | Java | 8 | 0 | 3 | 11 |
| [src/main/java/com/example/alipay/repository/fintech/BankCardRepository.java](/src/main/java/com/example/alipay/repository/fintech/BankCardRepository.java) | Java | 8 | 0 | 3 | 11 |
| [src/main/java/com/example/alipay/repository/fintech/BillRepository.java](/src/main/java/com/example/alipay/repository/fintech/BillRepository.java) | Java | 11 | 0 | 3 | 14 |
| [src/main/java/com/example/alipay/repository/fintech/UserAuditRepository.java](/src/main/java/com/example/alipay/repository/fintech/UserAuditRepository.java) | Java | 7 | 0 | 3 | 10 |
| [src/main/java/com/example/alipay/repository/fintech/UserRepository.java](/src/main/java/com/example/alipay/repository/fintech/UserRepository.java) | Java | 18 | 0 | 7 | 25 |
| [src/main/java/com/example/alipay/repository/site/GateEventRepository.java](/src/main/java/com/example/alipay/repository/site/GateEventRepository.java) | Java | 12 | 0 | 3 | 15 |
| [src/main/java/com/example/alipay/repository/site/GateRepository.java](/src/main/java/com/example/alipay/repository/site/GateRepository.java) | Java | 12 | 0 | 3 | 15 |
| [src/main/java/com/example/alipay/repository/site/StationRepository.java](/src/main/java/com/example/alipay/repository/site/StationRepository.java) | Java | 11 | 0 | 3 | 14 |
| [src/main/java/com/example/alipay/security/fintech/CustomUserDetailsService.java](/src/main/java/com/example/alipay/security/fintech/CustomUserDetailsService.java) | Java | 31 | 0 | 6 | 37 |
| [src/main/java/com/example/alipay/security/fintech/JwtAuthenticationFilter.java](/src/main/java/com/example/alipay/security/fintech/JwtAuthenticationFilter.java) | Java | 43 | 0 | 7 | 50 |
| [src/main/java/com/example/alipay/security/fintech/JwtUtil.java](/src/main/java/com/example/alipay/security/fintech/JwtUtil.java) | Java | 38 | 0 | 9 | 47 |
| [src/main/java/com/example/alipay/service/CollectionService.java](/src/main/java/com/example/alipay/service/CollectionService.java) | Java | 36 | 0 | 8 | 44 |
| [src/main/java/com/example/alipay/service/PaymentService.java](/src/main/java/com/example/alipay/service/PaymentService.java) | Java | 195 | 0 | 19 | 214 |
| [src/main/java/com/example/alipay/service/QrService.java](/src/main/java/com/example/alipay/service/QrService.java) | Java | 70 | 0 | 23 | 93 |
| [src/main/java/com/example/alipay/service/SmartAssistantService.java](/src/main/java/com/example/alipay/service/SmartAssistantService.java) | Java | 151 | 1 | 35 | 187 |
| [src/main/java/com/example/alipay/service/TravelService.java](/src/main/java/com/example/alipay/service/TravelService.java) | Java | 138 | 1 | 13 | 152 |
| [src/main/java/com/example/alipay/service/admin/AdminAuthService.java](/src/main/java/com/example/alipay/service/admin/AdminAuthService.java) | Java | 54 | 0 | 12 | 66 |
| [src/main/java/com/example/alipay/service/admin/AdminOperationLogService.java](/src/main/java/com/example/alipay/service/admin/AdminOperationLogService.java) | Java | 21 | 0 | 8 | 29 |
| [src/main/java/com/example/alipay/service/admin/AdminOperationLogger.java](/src/main/java/com/example/alipay/service/admin/AdminOperationLogger.java) | Java | 60 | 0 | 9 | 69 |
| [src/main/java/com/example/alipay/service/admin/DiscountPolicyService.java](/src/main/java/com/example/alipay/service/admin/DiscountPolicyService.java) | Java | 104 | 0 | 15 | 119 |
| [src/main/java/com/example/alipay/service/admin/UserManagementService.java](/src/main/java/com/example/alipay/service/admin/UserManagementService.java) | Java | 84 | 1 | 13 | 98 |
| [src/main/java/com/example/alipay/service/fintech/AssetService.java](/src/main/java/com/example/alipay/service/fintech/AssetService.java) | Java | 76 | 0 | 19 | 95 |
| [src/main/java/com/example/alipay/service/fintech/AuthService.java](/src/main/java/com/example/alipay/service/fintech/AuthService.java) | Java | 62 | 0 | 11 | 73 |
| [src/main/java/com/example/alipay/service/fintech/BillService.java](/src/main/java/com/example/alipay/service/fintech/BillService.java) | Java | 77 | 0 | 17 | 94 |
| [src/main/java/com/example/alipay/service/site/GateEventService.java](/src/main/java/com/example/alipay/service/site/GateEventService.java) | Java | 155 | 10 | 33 | 198 |
| [src/main/java/com/example/alipay/service/site/StationManagementService.java](/src/main/java/com/example/alipay/service/site/StationManagementService.java) | Java | 128 | 2 | 21 | 151 |
| [src/main/resources/application.properties](/src/main/resources/application.properties) | Java Properties | 18 | 12 | 7 | 37 |
| [src/test/java/com/example/alipay/service/PaymentServiceTest.java](/src/test/java/com/example/alipay/service/PaymentServiceTest.java) | Java | 108 | 0 | 20 | 128 |
[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md)

@ -1,15 +0,0 @@
# Diff Details
Date : 2026-01-05 15:04:31
Directory c:\\Users\\87641\\Downloads\\simple-alipay-backend\\src
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details
## Files
| filename | language | code | comment | blank | total |
| :--- | :--- | ---: | ---: | ---: | ---: |
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details

@ -1,2 +0,0 @@
"filename", "language", "", "comment", "blank", "total"
"Total", "-", , 0, 0, 0
1 filename language comment blank total
2 Total - 0 0 0

@ -1,19 +0,0 @@
# Diff Summary
Date : 2026-01-05 15:04:31
Directory c:\\Users\\87641\\Downloads\\simple-alipay-backend\\src
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)

@ -1,22 +0,0 @@
Date : 2026-01-05 15:04:31
Directory : c:\Users\87641\Downloads\simple-alipay-backend\src
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
Languages
+----------+------------+------------+------------+------------+------------+
| language | files | code | comment | blank | total |
+----------+------------+------------+------------+------------+------------+
+----------+------------+------------+------------+------------+------------+
Directories
+------+------------+------------+------------+------------+------------+
| path | files | code | comment | blank | total |
+------+------------+------------+------------+------------+------------+
+------+------------+------------+------------+------------+------------+
Files
+----------+----------+------------+------------+------------+------------+
| filename | language | code | comment | blank | total |
+----------+----------+------------+------------+------------+------------+
| Total | | 0 | 0 | 0 | 0 |
+----------+----------+------------+------------+------------+------------+

@ -1,73 +0,0 @@
"filename", "language", "Java Properties", "Java", "comment", "blank", "total"
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\SimpleAlipayBackendApplication.java", "Java", 0, 9, 0, 3, 12
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\DataLoader.java", "Java", 0, 51, 0, 5, 56
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\GlobalExceptionHandler.java", "Java", 0, 83, 0, 16, 99
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\fintech\SecurityConfig.java", "Java", 0, 77, 5, 9, 91
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\CollectionController.java", "Java", 0, 40, 0, 7, 47
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\PaymentController.java", "Java", 0, 68, 0, 8, 76
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\SmartAssistantController.java", "Java", 0, 29, 0, 9, 38
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\TravelController.java", "Java", 0, 41, 0, 8, 49
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\AdminAuthController.java", "Java", 0, 81, 0, 15, 96
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\AdminLogController.java", "Java", 0, 29, 0, 8, 37
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\DiscountPolicyController.java", "Java", 0, 87, 0, 14, 101
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\UserManagementController.java", "Java", 0, 72, 0, 13, 85
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\AssetController.java", "Java", 0, 61, 0, 12, 73
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\AuthController.java", "Java", 0, 124, 0, 16, 140
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\BillController.java", "Java", 0, 48, 0, 10, 58
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\site\GateEventController.java", "Java", 0, 82, 1, 15, 98
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\site\StationManagementController.java", "Java", 0, 136, 3, 20, 159
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\AssistantMessage.java", "Java", 0, 29, 1, 8, 38
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\CollectionQRCode.java", "Java", 0, 22, 0, 4, 26
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\Payment.java", "Java", 0, 35, 0, 4, 39
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\TravelPass.java", "Java", 0, 28, 1, 4, 33
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\TravelRecord.java", "Java", 0, 32, 1, 4, 37
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\UserAccount.java", "Java", 0, 22, 1, 4, 27
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\AdminOperationLog.java", "Java", 0, 22, 0, 8, 30
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\AdminUser.java", "Java", 0, 29, 1, 9, 39
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\CreateAdminRequest.java", "Java", 0, 8, 0, 3, 11
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\DiscountPolicy.java", "Java", 0, 27, 0, 8, 35
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\BankCard.java", "Java", 0, 14, 0, 3, 17
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\Bill.java", "Java", 0, 21, 0, 7, 28
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\User.java", "Java", 0, 39, 0, 11, 50
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\UserAudit.java", "Java", 0, 16, 0, 3, 19
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\UserStatus.java", "Java", 0, 7, 0, 2, 9
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\Gate.java", "Java", 0, 22, 0, 7, 29
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\GateEvent.java", "Java", 0, 28, 0, 9, 37
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\Station.java", "Java", 0, 21, 0, 6, 27
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\AssistantMessageRepository.java", "Java", 0, 7, 1, 2, 10
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\CollectionQRCodeRepository.java", "Java", 0, 5, 0, 3, 8
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\PaymentRepository.java", "Java", 0, 10, 0, 4, 14
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\TravelPassRepository.java", "Java", 0, 7, 0, 4, 11
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\TravelRecordRepository.java", "Java", 0, 7, 0, 4, 11
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\UserAccountRepository.java", "Java", 0, 14, 0, 6, 20
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\AdminOperationLogRepository.java", "Java", 0, 8, 0, 3, 11
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\AdminUserRepository.java", "Java", 0, 8, 0, 3, 11
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\DiscountPolicyRepository.java", "Java", 0, 8, 0, 3, 11
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\BankCardRepository.java", "Java", 0, 8, 0, 3, 11
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\BillRepository.java", "Java", 0, 11, 0, 3, 14
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\UserAuditRepository.java", "Java", 0, 7, 0, 3, 10
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\UserRepository.java", "Java", 0, 18, 0, 7, 25
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\GateEventRepository.java", "Java", 0, 12, 0, 3, 15
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\GateRepository.java", "Java", 0, 12, 0, 3, 15
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\StationRepository.java", "Java", 0, 11, 0, 3, 14
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\CustomUserDetailsService.java", "Java", 0, 31, 0, 6, 37
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\JwtAuthenticationFilter.java", "Java", 0, 43, 0, 7, 50
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\JwtUtil.java", "Java", 0, 38, 0, 9, 47
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\CollectionService.java", "Java", 0, 36, 0, 8, 44
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\PaymentService.java", "Java", 0, 195, 0, 19, 214
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\QrService.java", "Java", 0, 70, 0, 23, 93
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\SmartAssistantService.java", "Java", 0, 151, 1, 35, 187
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\TravelService.java", "Java", 0, 138, 1, 13, 152
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminAuthService.java", "Java", 0, 54, 0, 12, 66
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminOperationLogService.java", "Java", 0, 21, 0, 8, 29
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminOperationLogger.java", "Java", 0, 60, 0, 9, 69
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\DiscountPolicyService.java", "Java", 0, 104, 0, 15, 119
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\UserManagementService.java", "Java", 0, 84, 1, 13, 98
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\AssetService.java", "Java", 0, 76, 0, 19, 95
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\AuthService.java", "Java", 0, 62, 0, 11, 73
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\BillService.java", "Java", 0, 77, 0, 17, 94
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\site\GateEventService.java", "Java", 0, 155, 10, 33, 198
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\site\StationManagementService.java", "Java", 0, 128, 2, 21, 151
"c:\Users\87641\Downloads\simple-alipay-backend\src\main\resources\application.properties", "Java Properties", 18, 0, 12, 7, 37
"c:\Users\87641\Downloads\simple-alipay-backend\src\test\java\com\example\alipay\service\PaymentServiceTest.java", "Java", 0, 108, 0, 20, 128
"Total", "-", 18, 3324, 42, 654, 4038
1 filename language Java Properties Java comment blank total
2 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\SimpleAlipayBackendApplication.java Java 0 9 0 3 12
3 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\DataLoader.java Java 0 51 0 5 56
4 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\GlobalExceptionHandler.java Java 0 83 0 16 99
5 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\fintech\SecurityConfig.java Java 0 77 5 9 91
6 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\CollectionController.java Java 0 40 0 7 47
7 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\PaymentController.java Java 0 68 0 8 76
8 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\SmartAssistantController.java Java 0 29 0 9 38
9 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\TravelController.java Java 0 41 0 8 49
10 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\AdminAuthController.java Java 0 81 0 15 96
11 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\AdminLogController.java Java 0 29 0 8 37
12 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\DiscountPolicyController.java Java 0 87 0 14 101
13 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\UserManagementController.java Java 0 72 0 13 85
14 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\AssetController.java Java 0 61 0 12 73
15 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\AuthController.java Java 0 124 0 16 140
16 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\BillController.java Java 0 48 0 10 58
17 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\site\GateEventController.java Java 0 82 1 15 98
18 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\site\StationManagementController.java Java 0 136 3 20 159
19 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\AssistantMessage.java Java 0 29 1 8 38
20 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\CollectionQRCode.java Java 0 22 0 4 26
21 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\Payment.java Java 0 35 0 4 39
22 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\TravelPass.java Java 0 28 1 4 33
23 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\TravelRecord.java Java 0 32 1 4 37
24 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\UserAccount.java Java 0 22 1 4 27
25 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\AdminOperationLog.java Java 0 22 0 8 30
26 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\AdminUser.java Java 0 29 1 9 39
27 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\CreateAdminRequest.java Java 0 8 0 3 11
28 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\DiscountPolicy.java Java 0 27 0 8 35
29 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\BankCard.java Java 0 14 0 3 17
30 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\Bill.java Java 0 21 0 7 28
31 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\User.java Java 0 39 0 11 50
32 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\UserAudit.java Java 0 16 0 3 19
33 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\UserStatus.java Java 0 7 0 2 9
34 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\Gate.java Java 0 22 0 7 29
35 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\GateEvent.java Java 0 28 0 9 37
36 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\Station.java Java 0 21 0 6 27
37 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\AssistantMessageRepository.java Java 0 7 1 2 10
38 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\CollectionQRCodeRepository.java Java 0 5 0 3 8
39 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\PaymentRepository.java Java 0 10 0 4 14
40 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\TravelPassRepository.java Java 0 7 0 4 11
41 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\TravelRecordRepository.java Java 0 7 0 4 11
42 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\UserAccountRepository.java Java 0 14 0 6 20
43 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\AdminOperationLogRepository.java Java 0 8 0 3 11
44 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\AdminUserRepository.java Java 0 8 0 3 11
45 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\DiscountPolicyRepository.java Java 0 8 0 3 11
46 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\BankCardRepository.java Java 0 8 0 3 11
47 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\BillRepository.java Java 0 11 0 3 14
48 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\UserAuditRepository.java Java 0 7 0 3 10
49 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\UserRepository.java Java 0 18 0 7 25
50 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\GateEventRepository.java Java 0 12 0 3 15
51 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\GateRepository.java Java 0 12 0 3 15
52 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\StationRepository.java Java 0 11 0 3 14
53 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\CustomUserDetailsService.java Java 0 31 0 6 37
54 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\JwtAuthenticationFilter.java Java 0 43 0 7 50
55 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\JwtUtil.java Java 0 38 0 9 47
56 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\CollectionService.java Java 0 36 0 8 44
57 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\PaymentService.java Java 0 195 0 19 214
58 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\QrService.java Java 0 70 0 23 93
59 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\SmartAssistantService.java Java 0 151 1 35 187
60 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\TravelService.java Java 0 138 1 13 152
61 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminAuthService.java Java 0 54 0 12 66
62 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminOperationLogService.java Java 0 21 0 8 29
63 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminOperationLogger.java Java 0 60 0 9 69
64 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\DiscountPolicyService.java Java 0 104 0 15 119
65 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\UserManagementService.java Java 0 84 1 13 98
66 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\AssetService.java Java 0 76 0 19 95
67 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\AuthService.java Java 0 62 0 11 73
68 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\BillService.java Java 0 77 0 17 94
69 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\site\GateEventService.java Java 0 155 10 33 198
70 c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\site\StationManagementService.java Java 0 128 2 21 151
71 c:\Users\87641\Downloads\simple-alipay-backend\src\main\resources\application.properties Java Properties 18 0 12 7 37
72 c:\Users\87641\Downloads\simple-alipay-backend\src\test\java\com\example\alipay\service\PaymentServiceTest.java Java 0 108 0 20 128
73 Total - 18 3324 42 654 4038

File diff suppressed because one or more lines are too long

@ -1,60 +0,0 @@
# Summary
Date : 2026-01-05 15:04:31
Directory c:\\Users\\87641\\Downloads\\simple-alipay-backend\\src
Total : 71 files, 3342 codes, 42 comments, 654 blanks, all 4038 lines
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| Java | 70 | 3,324 | 30 | 647 | 4,001 |
| Java Properties | 1 | 18 | 12 | 7 | 37 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 71 | 3,342 | 42 | 654 | 4,038 |
| main | 70 | 3,234 | 42 | 634 | 3,910 |
| main\\java | 69 | 3,216 | 30 | 627 | 3,873 |
| main\\java\\com | 69 | 3,216 | 30 | 627 | 3,873 |
| main\\java\\com\\example | 69 | 3,216 | 30 | 627 | 3,873 |
| main\\java\\com\\example\\alipay | 69 | 3,216 | 30 | 627 | 3,873 |
| main\\java\\com\\example\\alipay (Files) | 1 | 9 | 0 | 3 | 12 |
| main\\java\\com\\example\\alipay\\config | 3 | 211 | 5 | 30 | 246 |
| main\\java\\com\\example\\alipay\\config (Files) | 2 | 134 | 0 | 21 | 155 |
| main\\java\\com\\example\\alipay\\config\\fintech | 1 | 77 | 5 | 9 | 91 |
| main\\java\\com\\example\\alipay\\controller | 13 | 898 | 4 | 155 | 1,057 |
| main\\java\\com\\example\\alipay\\controller (Files) | 4 | 178 | 0 | 32 | 210 |
| main\\java\\com\\example\\alipay\\controller\\admin | 4 | 269 | 0 | 50 | 319 |
| main\\java\\com\\example\\alipay\\controller\\fintech | 3 | 233 | 0 | 38 | 271 |
| main\\java\\com\\example\\alipay\\controller\\site | 2 | 218 | 4 | 35 | 257 |
| main\\java\\com\\example\\alipay\\model | 18 | 422 | 5 | 104 | 531 |
| main\\java\\com\\example\\alipay\\model (Files) | 6 | 168 | 4 | 28 | 200 |
| main\\java\\com\\example\\alipay\\model\\admin | 4 | 86 | 1 | 28 | 115 |
| main\\java\\com\\example\\alipay\\model\\fintech | 5 | 97 | 0 | 26 | 123 |
| main\\java\\com\\example\\alipay\\model\\site | 3 | 71 | 0 | 22 | 93 |
| main\\java\\com\\example\\alipay\\repository | 16 | 153 | 1 | 57 | 211 |
| main\\java\\com\\example\\alipay\\repository (Files) | 6 | 50 | 1 | 23 | 74 |
| main\\java\\com\\example\\alipay\\repository\\admin | 3 | 24 | 0 | 9 | 33 |
| main\\java\\com\\example\\alipay\\repository\\fintech | 4 | 44 | 0 | 16 | 60 |
| main\\java\\com\\example\\alipay\\repository\\site | 3 | 35 | 0 | 9 | 44 |
| main\\java\\com\\example\\alipay\\security | 3 | 112 | 0 | 22 | 134 |
| main\\java\\com\\example\\alipay\\security\\fintech | 3 | 112 | 0 | 22 | 134 |
| main\\java\\com\\example\\alipay\\service | 15 | 1,411 | 15 | 256 | 1,682 |
| main\\java\\com\\example\\alipay\\service (Files) | 5 | 590 | 2 | 98 | 690 |
| main\\java\\com\\example\\alipay\\service\\admin | 5 | 323 | 1 | 57 | 381 |
| main\\java\\com\\example\\alipay\\service\\fintech | 3 | 215 | 0 | 47 | 262 |
| main\\java\\com\\example\\alipay\\service\\site | 2 | 283 | 12 | 54 | 349 |
| main\\resources | 1 | 18 | 12 | 7 | 37 |
| test | 1 | 108 | 0 | 20 | 128 |
| test\\java | 1 | 108 | 0 | 20 | 128 |
| test\\java\\com | 1 | 108 | 0 | 20 | 128 |
| test\\java\\com\\example | 1 | 108 | 0 | 20 | 128 |
| test\\java\\com\\example\\alipay | 1 | 108 | 0 | 20 | 128 |
| test\\java\\com\\example\\alipay\\service | 1 | 108 | 0 | 20 | 128 |
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)

@ -1,134 +0,0 @@
Date : 2026-01-05 15:04:31
Directory : c:\Users\87641\Downloads\simple-alipay-backend\src
Total : 71 files, 3342 codes, 42 comments, 654 blanks, all 4038 lines
Languages
+-----------------+------------+------------+------------+------------+------------+
| language | files | code | comment | blank | total |
+-----------------+------------+------------+------------+------------+------------+
| Java | 70 | 3,324 | 30 | 647 | 4,001 |
| Java Properties | 1 | 18 | 12 | 7 | 37 |
+-----------------+------------+------------+------------+------------+------------+
Directories
+-----------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
| path | files | code | comment | blank | total |
+-----------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
| . | 71 | 3,342 | 42 | 654 | 4,038 |
| main | 70 | 3,234 | 42 | 634 | 3,910 |
| main\java | 69 | 3,216 | 30 | 627 | 3,873 |
| main\java\com | 69 | 3,216 | 30 | 627 | 3,873 |
| main\java\com\example | 69 | 3,216 | 30 | 627 | 3,873 |
| main\java\com\example\alipay | 69 | 3,216 | 30 | 627 | 3,873 |
| main\java\com\example\alipay (Files) | 1 | 9 | 0 | 3 | 12 |
| main\java\com\example\alipay\config | 3 | 211 | 5 | 30 | 246 |
| main\java\com\example\alipay\config (Files) | 2 | 134 | 0 | 21 | 155 |
| main\java\com\example\alipay\config\fintech | 1 | 77 | 5 | 9 | 91 |
| main\java\com\example\alipay\controller | 13 | 898 | 4 | 155 | 1,057 |
| main\java\com\example\alipay\controller (Files) | 4 | 178 | 0 | 32 | 210 |
| main\java\com\example\alipay\controller\admin | 4 | 269 | 0 | 50 | 319 |
| main\java\com\example\alipay\controller\fintech | 3 | 233 | 0 | 38 | 271 |
| main\java\com\example\alipay\controller\site | 2 | 218 | 4 | 35 | 257 |
| main\java\com\example\alipay\model | 18 | 422 | 5 | 104 | 531 |
| main\java\com\example\alipay\model (Files) | 6 | 168 | 4 | 28 | 200 |
| main\java\com\example\alipay\model\admin | 4 | 86 | 1 | 28 | 115 |
| main\java\com\example\alipay\model\fintech | 5 | 97 | 0 | 26 | 123 |
| main\java\com\example\alipay\model\site | 3 | 71 | 0 | 22 | 93 |
| main\java\com\example\alipay\repository | 16 | 153 | 1 | 57 | 211 |
| main\java\com\example\alipay\repository (Files) | 6 | 50 | 1 | 23 | 74 |
| main\java\com\example\alipay\repository\admin | 3 | 24 | 0 | 9 | 33 |
| main\java\com\example\alipay\repository\fintech | 4 | 44 | 0 | 16 | 60 |
| main\java\com\example\alipay\repository\site | 3 | 35 | 0 | 9 | 44 |
| main\java\com\example\alipay\security | 3 | 112 | 0 | 22 | 134 |
| main\java\com\example\alipay\security\fintech | 3 | 112 | 0 | 22 | 134 |
| main\java\com\example\alipay\service | 15 | 1,411 | 15 | 256 | 1,682 |
| main\java\com\example\alipay\service (Files) | 5 | 590 | 2 | 98 | 690 |
| main\java\com\example\alipay\service\admin | 5 | 323 | 1 | 57 | 381 |
| main\java\com\example\alipay\service\fintech | 3 | 215 | 0 | 47 | 262 |
| main\java\com\example\alipay\service\site | 2 | 283 | 12 | 54 | 349 |
| main\resources | 1 | 18 | 12 | 7 | 37 |
| test | 1 | 108 | 0 | 20 | 128 |
| test\java | 1 | 108 | 0 | 20 | 128 |
| test\java\com | 1 | 108 | 0 | 20 | 128 |
| test\java\com\example | 1 | 108 | 0 | 20 | 128 |
| test\java\com\example\alipay | 1 | 108 | 0 | 20 | 128 |
| test\java\com\example\alipay\service | 1 | 108 | 0 | 20 | 128 |
+-----------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
Files
+-----------------------------------------------------------------------------------------------------------------------------------+-----------------+------------+------------+------------+------------+
| filename | language | code | comment | blank | total |
+-----------------------------------------------------------------------------------------------------------------------------------+-----------------+------------+------------+------------+------------+
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\SimpleAlipayBackendApplication.java | Java | 9 | 0 | 3 | 12 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\DataLoader.java | Java | 51 | 0 | 5 | 56 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\GlobalExceptionHandler.java | Java | 83 | 0 | 16 | 99 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\config\fintech\SecurityConfig.java | Java | 77 | 5 | 9 | 91 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\CollectionController.java | Java | 40 | 0 | 7 | 47 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\PaymentController.java | Java | 68 | 0 | 8 | 76 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\SmartAssistantController.java | Java | 29 | 0 | 9 | 38 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\TravelController.java | Java | 41 | 0 | 8 | 49 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\AdminAuthController.java | Java | 81 | 0 | 15 | 96 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\AdminLogController.java | Java | 29 | 0 | 8 | 37 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\DiscountPolicyController.java | Java | 87 | 0 | 14 | 101 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\admin\UserManagementController.java | Java | 72 | 0 | 13 | 85 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\AssetController.java | Java | 61 | 0 | 12 | 73 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\AuthController.java | Java | 124 | 0 | 16 | 140 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\fintech\BillController.java | Java | 48 | 0 | 10 | 58 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\site\GateEventController.java | Java | 82 | 1 | 15 | 98 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\controller\site\StationManagementController.java | Java | 136 | 3 | 20 | 159 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\AssistantMessage.java | Java | 29 | 1 | 8 | 38 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\CollectionQRCode.java | Java | 22 | 0 | 4 | 26 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\Payment.java | Java | 35 | 0 | 4 | 39 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\TravelPass.java | Java | 28 | 1 | 4 | 33 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\TravelRecord.java | Java | 32 | 1 | 4 | 37 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\UserAccount.java | Java | 22 | 1 | 4 | 27 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\AdminOperationLog.java | Java | 22 | 0 | 8 | 30 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\AdminUser.java | Java | 29 | 1 | 9 | 39 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\CreateAdminRequest.java | Java | 8 | 0 | 3 | 11 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\admin\DiscountPolicy.java | Java | 27 | 0 | 8 | 35 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\BankCard.java | Java | 14 | 0 | 3 | 17 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\Bill.java | Java | 21 | 0 | 7 | 28 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\User.java | Java | 39 | 0 | 11 | 50 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\UserAudit.java | Java | 16 | 0 | 3 | 19 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\fintech\UserStatus.java | Java | 7 | 0 | 2 | 9 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\Gate.java | Java | 22 | 0 | 7 | 29 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\GateEvent.java | Java | 28 | 0 | 9 | 37 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\model\site\Station.java | Java | 21 | 0 | 6 | 27 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\AssistantMessageRepository.java | Java | 7 | 1 | 2 | 10 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\CollectionQRCodeRepository.java | Java | 5 | 0 | 3 | 8 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\PaymentRepository.java | Java | 10 | 0 | 4 | 14 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\TravelPassRepository.java | Java | 7 | 0 | 4 | 11 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\TravelRecordRepository.java | Java | 7 | 0 | 4 | 11 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\UserAccountRepository.java | Java | 14 | 0 | 6 | 20 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\AdminOperationLogRepository.java | Java | 8 | 0 | 3 | 11 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\AdminUserRepository.java | Java | 8 | 0 | 3 | 11 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\admin\DiscountPolicyRepository.java | Java | 8 | 0 | 3 | 11 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\BankCardRepository.java | Java | 8 | 0 | 3 | 11 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\BillRepository.java | Java | 11 | 0 | 3 | 14 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\UserAuditRepository.java | Java | 7 | 0 | 3 | 10 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\fintech\UserRepository.java | Java | 18 | 0 | 7 | 25 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\GateEventRepository.java | Java | 12 | 0 | 3 | 15 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\GateRepository.java | Java | 12 | 0 | 3 | 15 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\repository\site\StationRepository.java | Java | 11 | 0 | 3 | 14 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\CustomUserDetailsService.java | Java | 31 | 0 | 6 | 37 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\JwtAuthenticationFilter.java | Java | 43 | 0 | 7 | 50 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\security\fintech\JwtUtil.java | Java | 38 | 0 | 9 | 47 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\CollectionService.java | Java | 36 | 0 | 8 | 44 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\PaymentService.java | Java | 195 | 0 | 19 | 214 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\QrService.java | Java | 70 | 0 | 23 | 93 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\SmartAssistantService.java | Java | 151 | 1 | 35 | 187 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\TravelService.java | Java | 138 | 1 | 13 | 152 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminAuthService.java | Java | 54 | 0 | 12 | 66 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminOperationLogService.java | Java | 21 | 0 | 8 | 29 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\AdminOperationLogger.java | Java | 60 | 0 | 9 | 69 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\DiscountPolicyService.java | Java | 104 | 0 | 15 | 119 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\admin\UserManagementService.java | Java | 84 | 1 | 13 | 98 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\AssetService.java | Java | 76 | 0 | 19 | 95 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\AuthService.java | Java | 62 | 0 | 11 | 73 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\fintech\BillService.java | Java | 77 | 0 | 17 | 94 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\site\GateEventService.java | Java | 155 | 10 | 33 | 198 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\java\com\example\alipay\service\site\StationManagementService.java | Java | 128 | 2 | 21 | 151 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\main\resources\application.properties | Java Properties | 18 | 12 | 7 | 37 |
| c:\Users\87641\Downloads\simple-alipay-backend\src\test\java\com\example\alipay\service\PaymentServiceTest.java | Java | 108 | 0 | 20 | 128 |
| Total | | 3,342 | 42 | 654 | 4,038 |
+-----------------------------------------------------------------------------------------------------------------------------------+-----------------+------------+------------+------------+------------+

@ -1,8 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="simple-alipay-backend" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="simple-alipay-backend" options="-parameters" />
</option>
</component>
</project>

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="hewendi@localhost" uuid="a83a0cb9-1efd-49a3-a7ba-278fcebb7c0b">
<driver-ref>sqlserver.ms</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>$PROJECT_DIR$/src/main/resources/application.properties</remarks>
<jdbc-driver>com.microsoft.sqlserver.jdbc.SQLServerDriver</jdbc-driver>
<jdbc-url>jdbc:sqlserver://localhost:1433;databaseName=hewendi</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
</component>
</project>

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

@ -1,13 +0,0 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -1,3 +0,0 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar

@ -1,3 +0,0 @@
{
"java.compile.nullAnalysis.mode": "automatic"
}

@ -1,444 +0,0 @@
# 项目完整API接口文档
## 📋 项目概述
- **项目名称**: 支付宝后端 + 金融科技集成系统
- **服务地址**: `http://localhost:8080`
- **技术栈**: Spring Boot 3.4.12 + SQL Server + JWT
---
## 🔐 支付宝核心模块
### 支付模块 (Payment)
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| POST | `/api/pay/execute` | 执行支付 | ❌ 无需认证 |
**请求体:**
```json
{
"fromUser": "alice",
"toMerchant": "bob",
"amount": "10.00",
"method": "balance"
}
```
### 收款码模块 (Collection)
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| POST | `/api/collect/create` | 创建收款码 | ❌ 无需认证 |
| POST | `/api/collect/refresh/{id}` | 刷新收款码 | ❌ 无需认证 |
| POST | `/api/collect/parseFromBase64` | 解析二维码 | ❌ 无需认证 |
**创建收款码请求体:**
```json
{
"merchantId": "bob",
"validSeconds": "120"
}
```
### 出行模块 (Travel)
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| POST | `/api/travel/open` | 开通出行码 | ❌ 无需认证 |
| POST | `/api/travel/entry` | 进站 | ❌ 无需认证 |
| POST | `/api/travel/exit/{recordId}` | 出站 | ❌ 无需认证 |
| GET | `/api/travel/records/{username}` | 查询出行记录 | ❌ 无需认证 |
**开通出行码请求体:**
```json
{
"username": "alice",
"city": "Beijing",
"line": "Line1",
"payment": "balance"
}
```
### 智能助手模块 (Smart Assistant)
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/assistant/welcome` | 欢迎语 | ❌ 无需认证 |
| POST | `/api/assistant/chat` | 智能对话 | ❌ 无需认证 |
| GET | `/api/assistant/analysis/monthly` | 月度分析 | ❌ 无需认证 |
| GET | `/api/assistant/analysis/alerts` | 异常支出提醒 | ❌ 无需认证 |
**智能对话请求参数:**
- `username`: 用户名
- `content`: 对话内容
---
## 💰 金融科技模块
### 用户认证模块 (Auth)
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| POST | `/api/fintech/auth/register` | 用户注册 | ❌ 无需认证 |
| POST | `/api/fintech/auth/login` | 用户登录 | ❌ 无需认证 |
**注册请求体:**
```json
{
"username": "testuser",
"password": "123456",
"phone": "13800138000"
}
```
**登录请求体:**
```json
{
"username": "testuser",
"password": "123456"
}
```
**登录响应:**
```json
{
"message": "登录成功",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userId": 1,
"username": "testuser",
"balance": 0.00
}
```
### 资产管理模块 (Assets) - 需要认证
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/fintech/assets/balance/{userId}` | 查询余额 | ✅ Bearer Token |
| POST | `/api/fintech/assets/cards` | 添加银行卡 | ✅ Bearer Token |
| GET | `/api/fintech/assets/cards/{userId}` | 查询银行卡 | ✅ Bearer Token |
| POST | `/api/fintech/assets/transfer` | 转账 | ✅ Bearer Token |
**添加银行卡请求体:**
```json
{
"userId": 1,
"bankName": "中国银行",
"cardNumber": "6222021234567890123",
"isDefault": true
}
```
**转账请求体:**
```json
{
"fromUserId": 1,
"toUserId": 2,
"amount": 100.00
}
```
### 账单管理模块 (Bills) - 需要认证
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/fintech/bills/{userId}` | 查询账单 | ✅ Bearer Token |
| GET | `/api/fintech/bills/{userId}/type/{type}` | 按类型查询 | ✅ Bearer Token |
| GET | `/api/fintech/bills/{userId}/overview` | 账单概览 | ✅ Bearer Token |
| POST | `/api/fintech/bills/create` | 创建账单 | ✅ Bearer Token |
**创建账单请求体:**
```json
{
"userId": 1,
"amount": 100.00,
"type": "EXPENDITURE",
"category": "餐饮",
"remark": "午餐消费"
}
```
---
## 🔐 认证说明
### JWT令牌使用
- **获取方式**: 通过登录接口获取
- **使用方式**: 在请求头中添加 `Authorization: Bearer <token>`
- **有效期**: 24小时
### 认证要求
- **无需认证**: 支付宝模块所有API + 金融科技认证API
- **需要认证**: 金融科技资产和账单管理API
---
## 📊 数据库表结构
### 支付宝模块表
- `user_account` - 用户账户
- `payment` - 支付记录
- `collection_qr_code` - 收款二维码
- `travel_pass` - 出行码
- `travel_record` - 出行记录
- `assistant_message` - 智能助手对话
### 金融科技模块表
- `fintech_users` - 金融科技用户
- `bank_card` - 银行卡
- `bill` - 账单
- `user_audit` - 用户审核
---
## 🚀 快速测试流程
### 支付宝模块测试
1. 支付: `POST /api/pay/execute`
2. 收款码: `POST /api/collect/create`
3. 出行: `POST /api/travel/open`
4. 智能助手: `GET /api/assistant/welcome`
### 金融科技模块测试
1. 注册: `POST /api/fintech/auth/register`
2. 登录: `POST /api/fintech/auth/login` (获取token)
3. 查询余额: `GET /api/fintech/assets/balance/1` (使用token)
4. 添加银行卡: `POST /api/fintech/assets/cards` (使用token)
5. 转账: `POST /api/fintech/assets/transfer` (使用token)
---
## 🏢 后台管理与权限模块
### 管理员认证模块 (Admin Auth)
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| POST | `/api/admin/auth/login` | 管理员登录 | ❌ 无需认证 |
| POST | `/api/admin/auth/create` | 创建管理员 | ❌ 无需认证 |
**管理员登录请求体:**
```json
{
"username": "admin",
"password": "admin123"
}
```
**创建管理员请求体:**
```json
{
"username": "site_manager",
"password": "manager123",
"role": "SITE_MANAGER"
}
```
### 用户信息管理模块 (User Management) - 需要SUPER_ADMIN角色
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/admin/users` | 获取所有用户 | ✅ SUPER_ADMIN |
| GET | `/api/admin/users/{userId}` | 获取用户详情 | ✅ SUPER_ADMIN |
| POST | `/api/admin/users/{userId}/disable` | 禁用用户 | ✅ SUPER_ADMIN |
| POST | `/api/admin/users/{userId}/enable` | 启用用户 | ✅ SUPER_ADMIN |
| GET | `/api/admin/users/audits/pending` | 获取待审核用户 | ✅ SUPER_ADMIN |
| POST | `/api/admin/users/audits/{userId}/approve` | 通过用户审核 | ✅ SUPER_ADMIN |
| POST | `/api/admin/users/audits/{userId}/reject` | 驳回用户审核 | ✅ SUPER_ADMIN |
**驳回用户审核请求体:**
```json
{
"rejectReason": "身份证照片不清晰"
}
```
### 折扣策略管理模块 (Discount Policy) - 需要SUPER_ADMIN角色
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/admin/policies` | 获取所有策略 | ✅ SUPER_ADMIN |
| GET | `/api/admin/policies/active` | 获取活跃策略 | ✅ SUPER_ADMIN |
| GET | `/api/admin/policies/{policyId}` | 获取策略详情 | ✅ SUPER_ADMIN |
| POST | `/api/admin/policies` | 创建策略 | ✅ SUPER_ADMIN |
| PUT | `/api/admin/policies/{policyId}` | 更新策略 | ✅ SUPER_ADMIN |
| POST | `/api/admin/policies/{policyId}/enable` | 启用策略 | ✅ SUPER_ADMIN |
| POST | `/api/admin/policies/{policyId}/disable` | 禁用策略 | ✅ SUPER_ADMIN |
| DELETE | `/api/admin/policies/{policyId}` | 删除策略 | ✅ SUPER_ADMIN |
| GET | `/api/admin/policies/line/{line}` | 按线路查询策略 | ✅ SUPER_ADMIN |
**创建折扣策略请求体:**
```json
{
"policyName": "学生优惠",
"description": "学生用户享受8折优惠",
"discountRate": 0.8,
"applicableUserType": "STUDENT",
"applicableLines": "Line1,Line2",
"startTime": "2025-01-01T00:00:00",
"endTime": "2025-12-31T23:59:59",
"enabled": true
}
```
---
## 🚉 站点管理模块
### 站点管理模块 (Station Management) - 需要SITE_MANAGER角色
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/site/stations` | 获取所有站点 | ✅ SITE_MANAGER |
| GET | `/api/site/stations/active` | 获取活跃站点 | ✅ SITE_MANAGER |
| GET | `/api/site/stations/{stationId}` | 获取站点详情 | ✅ SITE_MANAGER |
| POST | `/api/site/stations` | 创建站点 | ✅ SITE_MANAGER |
| PUT | `/api/site/stations/{stationId}` | 更新站点 | ✅ SITE_MANAGER |
| POST | `/api/site/stations/{stationId}/enable` | 启用站点 | ✅ SITE_MANAGER |
| POST | `/api/site/stations/{stationId}/disable` | 禁用站点 | ✅ SITE_MANAGER |
| GET | `/api/site/stations/{stationId}/gates` | 获取站点闸机 | ✅ SITE_MANAGER |
| GET | `/api/site/stations/city/{city}` | 按城市查询站点 | ✅ SITE_MANAGER |
| GET | `/api/site/stations/line/{line}` | 按线路查询站点 | ✅ SITE_MANAGER |
**创建站点请求体:**
```json
{
"stationCode": "S001",
"stationName": "北京站",
"city": "北京",
"line": "Line1",
"location": "北京市东城区",
"description": "北京地铁1号线站点",
"enabled": true
}
```
### 闸机管理模块 (Gate Management) - 需要SITE_MANAGER角色
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| GET | `/api/site/gates/direction/{direction}` | 按方向查询闸机 | ✅ SITE_MANAGER |
| POST | `/api/site/gates` | 创建闸机 | ✅ SITE_MANAGER |
| PUT | `/api/site/gates/{gateId}` | 更新闸机 | ✅ SITE_MANAGER |
| POST | `/api/site/gates/{gateId}/enable` | 启用闸机 | ✅ SITE_MANAGER |
| POST | `/api/site/gates/{gateId}/disable` | 禁用闸机 | ✅ SITE_MANAGER |
**创建闸机请求体:**
```json
{
"stationId": 1,
"gateCode": "G001",
"gateName": "北京站进站口A",
"direction": "ENTRY",
"location": "A口",
"description": "北京站主要进站口",
"enabled": true,
"status": "ONLINE"
}
```
### 闸机扫码与事件处理模块 (Gate Events) - 无需认证
| 方法 | 路径 | 描述 | 认证 |
|------|------|------|------|
| POST | `/api/site/gates/{gateCode}/entry` | 进站扫码 | ❌ 无需认证 |
| POST | `/api/site/gates/{gateCode}/exit` | 出站扫码 | ❌ 无需认证 |
| GET | `/api/site/gates/events/gate/{gateId}` | 获取闸机事件 | ✅ SITE_MANAGER |
| GET | `/api/site/gates/events/user/{userId}` | 获取用户事件 | ✅ SITE_MANAGER |
| GET | `/api/site/gates/events/errors` | 获取错误事件 | ✅ SITE_MANAGER |
**进站扫码请求体:**
```json
{
"qrCode": "123456"
}
```
**出站扫码请求体:**
```json
{
"qrCode": "123456"
}
```
---
## 🔐 认证说明
### JWT令牌使用
- **获取方式**: 通过登录接口获取
- **使用方式**: 在请求头中添加 `Authorization: Bearer <token>`
- **有效期**: 24小时
### 角色权限
- **SUPER_ADMIN**: 可以访问所有管理功能
- **SITE_MANAGER**: 可以访问站点管理功能
- **普通用户**: 只能访问金融科技功能
### 认证要求
- **无需认证**: 支付宝模块所有API + 金融科技认证API + 管理员认证API + 闸机扫码API
- **需要认证**: 金融科技资产和账单管理API
- **需要角色**: 管理员和站点管理API需要相应角色
---
## 📊 数据库表结构
### 支付宝模块表
- `user_account` - 用户账户
- `payment` - 支付记录
- `collection_qr_code` - 收款二维码
- `travel_pass` - 出行码
- `travel_record` - 出行记录
- `assistant_message` - 智能助手对话
### 金融科技模块表
- `fintech_users` - 金融科技用户
- `bank_card` - 银行卡
- `bill` - 账单
- `user_audit` - 用户审核
### 后台管理模块表
- `admin_users` - 管理员用户
- `discount_policies` - 折扣策略
- `admin_operation_logs` - 管理员操作日志
### 站点管理模块表
- `stations` - 站点信息
- `gates` - 闸机信息
- `gate_events` - 闸机事件记录
---
## 🚀 快速测试流程
### 支付宝模块测试
1. 支付: `POST /api/pay/execute`
2. 收款码: `POST /api/collect/create`
3. 出行: `POST /api/travel/open`
4. 智能助手: `GET /api/assistant/welcome`
### 金融科技模块测试
1. 注册: `POST /api/fintech/auth/register`
2. 登录: `POST /api/fintech/auth/login` (获取token)
3. 查询余额: `GET /api/fintech/assets/balance/1` (使用token)
4. 添加银行卡: `POST /api/fintech/assets/cards` (使用token)
5. 转账: `POST /api/fintech/assets/transfer` (使用token)
### 后台管理模块测试
1. 创建管理员: `POST /api/admin/auth/create`
2. 管理员登录: `POST /api/admin/auth/login` (获取管理员token)
3. 查看用户: `GET /api/admin/users` (使用管理员token)
4. 管理折扣策略: `POST /api/admin/policies` (使用管理员token)
### 站点管理模块测试
1. 创建站点: `POST /api/site/stations` (使用管理员token)
2. 创建闸机: `POST /api/site/gates` (使用管理员token)
3. 进站扫码: `POST /api/site/gates/G001/entry` (无需token)
4. 出站扫码: `POST /api/site/gates/G001/exit` (无需token)
---
## 📝 注意事项
1. **端口**: 默认使用 8080 端口
2. **数据库**: SQL Server自动创建表结构
3. **JWT**: 登录后获取的token用于访问受保护API
4. **角色权限**: 不同角色有不同的访问权限
5. **闸机扫码**: 进站出站扫码无需认证,直接调用
6. **错误处理**: 所有API都包含错误响应处理
7. **数据验证**: 请求参数会自动验证
---
**文档版本**: v2.0
**最后更新**: 2025/11/30

@ -1,317 +0,0 @@
# 项目数据库表结构文档
## 📋 数据库概述
- **数据库类型**: SQL Server
- **数据库名称**: hewendi
- **表总数**: 15个表
- **模块划分**: 4大模块
---
## 🏦 支付宝核心模块表
### 1. `user_account` - 用户账户表
**用途**: 存储支付宝用户账户信息
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| username | VARCHAR(255) | NOT NULL, UNIQUE | 用户名 |
| balance | DECIMAL(10,2) | DEFAULT 0.00 | 账户余额 |
| created_at | DATETIME | | 创建时间 |
| updated_at | DATETIME | | 更新时间 |
### 2. `payment` - 支付记录表
**用途**: 存储支付交易记录
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| from_user | VARCHAR(255) | NOT NULL | 付款方 |
| to_merchant | VARCHAR(255) | NOT NULL | 收款方 |
| amount | DECIMAL(10,2) | NOT NULL | 支付金额 |
| method | VARCHAR(50) | | 支付方式 |
| status | VARCHAR(50) | | 支付状态 |
| created_at | DATETIME | | 创建时间 |
### 3. `collection_qr_code` - 收款二维码表
**用途**: 存储收款二维码信息
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| merchant_id | VARCHAR(255) | NOT NULL | 商户ID |
| qr_code_content | TEXT | | 二维码内容 |
| valid_seconds | INT | | 有效时间(秒) |
| created_at | DATETIME | | 创建时间 |
| expires_at | DATETIME | | 过期时间 |
### 4. `travel_pass` - 出行码表
**用途**: 存储用户出行码信息
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| username | VARCHAR(255) | NOT NULL | 用户名 |
| city | VARCHAR(100) | | 城市 |
| line | VARCHAR(100) | | 线路 |
| payment | VARCHAR(50) | | 支付方式 |
| status | VARCHAR(50) | | 状态 |
| created_at | DATETIME | | 创建时间 |
### 5. `travel_record` - 出行记录表
**用途**: 存储用户出行记录
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| username | VARCHAR(255) | NOT NULL | 用户名 |
| entry_station | VARCHAR(255) | | 进站站点 |
| exit_station | VARCHAR(255) | | 出站站点 |
| fare | DECIMAL(8,2) | | 费用 |
| status | VARCHAR(50) | | 状态 |
| entry_time | DATETIME | | 进站时间 |
| exit_time | DATETIME | | 出站时间 |
### 6. `assistant_message` - 智能助手对话表
**用途**: 存储智能助手对话记录
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| username | VARCHAR(255) | NOT NULL | 用户名 |
| content | TEXT | | 对话内容 |
| role | VARCHAR(50) | | 角色(user/assistant) |
| created_at | DATETIME | | 创建时间 |
---
## 💰 金融科技模块表
### 7. `fintech_users` - 金融科技用户表
**用途**: 存储金融科技用户信息
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| username | VARCHAR(255) | NOT NULL, UNIQUE | 用户名 |
| password | VARCHAR(255) | NOT NULL | 密码 |
| phone | VARCHAR(20) | | 手机号 |
| balance | DECIMAL(10,2) | DEFAULT 0.00 | 余额 |
| status | VARCHAR(50) | | 用户状态 |
| enabled | BIT | DEFAULT 1 | 是否启用 |
| created_at | DATETIME | | 创建时间 |
| updated_at | DATETIME | | 更新时间 |
### 8. `bank_card` - 银行卡表
**用途**: 存储用户绑定的银行卡信息
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| user_id | BIGINT | NOT NULL | 用户ID |
| bank_name | VARCHAR(255) | NOT NULL | 银行名称 |
| card_number | VARCHAR(50) | NOT NULL | 卡号 |
| is_default | BIT | DEFAULT 0 | 是否默认卡 |
| created_at | DATETIME | | 创建时间 |
### 9. `bill` - 账单表
**用途**: 存储用户账单记录
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| user_id | BIGINT | NOT NULL | 用户ID |
| amount | DECIMAL(10,2) | NOT NULL | 金额 |
| type | VARCHAR(50) | | 类型(收入/支出) |
| category | VARCHAR(100) | | 分类 |
| remark | VARCHAR(500) | | 备注 |
| created_at | DATETIME | | 创建时间 |
### 10. `user_audit` - 用户审核表
**用途**: 存储用户审核记录
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| user_id | BIGINT | NOT NULL | 用户ID |
| audit_status | VARCHAR(50) | | 审核状态 |
| reject_reason | VARCHAR(500) | | 驳回原因 |
| created_at | DATETIME | | 创建时间 |
| updated_at | DATETIME | | 更新时间 |
---
## 🏢 后台管理模块表
### 11. `admin_users` - 管理员用户表
**用途**: 存储后台管理员账号信息
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| username | VARCHAR(255) | NOT NULL, UNIQUE | 用户名 |
| password | VARCHAR(255) | NOT NULL | 密码 |
| role | VARCHAR(50) | | 角色(SUPER_ADMIN/SITE_MANAGER) |
| enabled | BIT | DEFAULT 1 | 是否启用 |
### 12. `discount_policies` - 折扣策略表
**用途**: 存储折扣策略配置
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| policy_name | VARCHAR(255) | | 策略名称 |
| description | VARCHAR(500) | | 策略描述 |
| discount_rate | DECIMAL(5,2) | | 折扣率(0.8表示8折) |
| applicable_user_type | VARCHAR(100) | | 适用用户类型 |
| applicable_lines | VARCHAR(500) | | 适用线路 |
| start_time | DATETIME | | 生效时间 |
| end_time | DATETIME | | 失效时间 |
| enabled | BIT | DEFAULT 1 | 是否启用 |
| created_at | DATETIME | | 创建时间 |
| updated_at | DATETIME | | 更新时间 |
### 13. `admin_operation_logs` - 管理员操作日志表
**用途**: 记录管理员操作日志
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| admin_id | BIGINT | NOT NULL | 管理员ID |
| admin_username | VARCHAR(255) | | 管理员用户名 |
| operation_type | VARCHAR(100) | | 操作类型 |
| target_type | VARCHAR(100) | | 目标类型 |
| target_id | BIGINT | | 目标ID |
| operation_details | VARCHAR(1000) | | 操作详情 |
| ip_address | VARCHAR(50) | | IP地址 |
| operation_time | DATETIME | | 操作时间 |
| success | BIT | DEFAULT 1 | 是否成功 |
| error_message | VARCHAR(500) | | 错误信息 |
---
## 🚉 站点管理模块表
### 14. `stations` - 站点信息表
**用途**: 存储地铁站点信息
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| station_code | VARCHAR(50) | | 站点编码 |
| station_name | VARCHAR(255) | | 站点名称 |
| city | VARCHAR(100) | | 所在城市 |
| line | VARCHAR(100) | | 所属线路 |
| location | VARCHAR(500) | | 地理位置 |
| description | VARCHAR(500) | | 站点描述 |
| enabled | BIT | DEFAULT 1 | 是否启用 |
| created_at | DATETIME | | 创建时间 |
| updated_at | DATETIME | | 更新时间 |
### 15. `gates` - 闸机信息表
**用途**: 存储闸机设备信息
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| station_id | BIGINT | NOT NULL | 所属站点ID |
| gate_code | VARCHAR(50) | | 闸机编码 |
| gate_name | VARCHAR(255) | | 闸机名称 |
| direction | VARCHAR(50) | | 方向(ENTRY/EXIT) |
| location | VARCHAR(500) | | 具体位置 |
| description | VARCHAR(500) | | 闸机描述 |
| enabled | BIT | DEFAULT 1 | 是否启用 |
| status | VARCHAR(50) | | 状态(ONLINE/OFFLINE/MAINTENANCE) |
| created_at | DATETIME | | 创建时间 |
| updated_at | DATETIME | | 更新时间 |
### 16. `gate_events` - 闸机事件记录表
**用途**: 记录闸机扫码事件
| 字段名 | 类型 | 约束 | 描述 |
|--------|------|------|------|
| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
| gate_id | BIGINT | | 闸机ID |
| gate_code | VARCHAR(50) | | 闸机编码 |
| user_id | BIGINT | | 用户ID |
| username | VARCHAR(255) | | 用户名 |
| event_type | VARCHAR(50) | | 事件类型(ENTRY/EXIT/ERROR) |
| qr_code | VARCHAR(500) | | 扫描的二维码内容 |
| fare | DECIMAL(8,2) | | 费用 |
| status | VARCHAR(50) | | 状态(SUCCESS/FAILED/PENDING) |
| error_code | VARCHAR(100) | | 错误代码 |
| error_message | VARCHAR(500) | | 错误信息 |
| event_time | DATETIME | | 事件时间 |
| processed_time | DATETIME | | 处理时间 |
| transaction_id | VARCHAR(100) | | 交易ID |
| remark | VARCHAR(500) | | 备注 |
---
## 🔗 表关系说明
### 主要关系
1. **用户关系**:
- `fintech_users``bank_card` (一对多)
- `fintech_users``bill` (一对多)
- `fintech_users``user_audit` (一对一)
2. **站点关系**:
- `stations``gates` (一对多)
- `gates``gate_events` (一对多)
3. **管理关系**:
- `admin_users``admin_operation_logs` (一对多)
### 业务逻辑关系
- 用户通过 `fintech_users` 表进行金融科技业务
- 管理员通过 `admin_users` 表进行后台管理
- 闸机扫码通过 `gate_events` 表记录事件
- 折扣策略通过 `discount_policies` 表配置
---
## 📊 索引建议
### 建议创建的索引
```sql
-- 用户相关索引
CREATE INDEX idx_fintech_users_username ON fintech_users(username);
CREATE INDEX idx_fintech_users_phone ON fintech_users(phone);
CREATE INDEX idx_bank_card_user_id ON bank_card(user_id);
CREATE INDEX idx_bill_user_id ON bill(user_id);
-- 管理员相关索引
CREATE INDEX idx_admin_users_username ON admin_users(username);
CREATE INDEX idx_admin_logs_admin_id ON admin_operation_logs(admin_id);
CREATE INDEX idx_admin_logs_operation_time ON admin_operation_logs(operation_time);
-- 站点相关索引
CREATE INDEX idx_stations_station_code ON stations(station_code);
CREATE INDEX idx_gates_station_id ON gates(station_id);
CREATE INDEX idx_gates_gate_code ON gates(gate_code);
CREATE INDEX idx_gate_events_gate_id ON gate_events(gate_id);
CREATE INDEX idx_gate_events_user_id ON gate_events(user_id);
CREATE INDEX idx_gate_events_event_time ON gate_events(event_time);
```
---
## 🎯 总结
### 数据库特点
- **模块化设计**: 4大模块15个表结构清晰
- **业务完整**: 覆盖支付、金融、管理、站点全流程
- **扩展性强**: 预留字段支持未来功能扩展
- **性能优化**: 合理的索引设计和关系映射
### 数据一致性
- 所有表都有主键和必要的约束
- 时间戳字段记录操作时间
- 状态字段支持业务状态管理
- 外键关系保证数据完整性
**文档版本**: v1.0
**最后更新**: 2025/11/30

@ -1,453 +0,0 @@
# 支付宝后端 + 金融科技集成系统 - 项目需求文档
## 📋 项目概述
### 项目名称
**支付宝后端 + 金融科技集成系统**
### 项目描述
本项目是一个综合性的后端系统集成了支付宝核心功能、金融科技服务、后台管理权限控制和站点管理功能。系统采用模块化设计支持多角色权限管理提供完整的API接口服务。
### 项目目标
1. 提供完整的支付宝支付、收款、出行等核心功能
2. 集成金融科技服务,支持用户资产管理
3. 实现后台管理权限控制,支持多角色管理
4. 提供站点管理功能,支持闸机扫码和事件处理
5. 构建可扩展、高性能的微服务架构
---
## 🎯 功能需求
### 一、支付宝核心模块
#### 1.1 支付功能
- **功能描述**: 支持用户之间的支付交易
- **核心功能**:
- 余额支付
- 支付状态管理
- 交易记录存储
- **API接口**: `POST /api/pay/execute`
#### 1.2 收款码功能
- **功能描述**: 生成和管理收款二维码
- **核心功能**:
- 动态生成收款码
- 二维码有效期管理
- 二维码解析服务
- **API接口**:
- `POST /api/collect/create`
- `POST /api/collect/refresh/{id}`
- `POST /api/collect/parseFromBase64`
#### 1.3 出行功能
- **功能描述**: 地铁出行码管理和计费
- **核心功能**:
- 开通出行码
- 进站出站记录
- 自动计费扣款
- 出行记录查询
- **API接口**:
- `POST /api/travel/open`
- `POST /api/travel/entry`
- `POST /api/travel/exit/{recordId}`
- `GET /api/travel/records/{username}`
#### 1.4 智能助手功能
- **功能描述**: AI智能客服和财务分析
- **核心功能**:
- 智能对话服务
- 月度消费分析
- 异常支出提醒
- **API接口**:
- `GET /api/assistant/welcome`
- `POST /api/assistant/chat`
- `GET /api/assistant/analysis/monthly`
- `GET /api/assistant/analysis/alerts`
### 二、金融科技模块
#### 2.1 用户认证
- **功能描述**: 用户注册、登录和身份验证
- **核心功能**:
- 用户注册
- 用户登录
- JWT令牌管理
- **API接口**:
- `POST /api/fintech/auth/register`
- `POST /api/fintech/auth/login`
#### 2.2 资产管理
- **功能描述**: 用户余额和银行卡管理
- **核心功能**:
- 余额查询
- 银行卡绑定
- 转账功能
- **API接口**:
- `GET /api/fintech/assets/balance/{userId}`
- `POST /api/fintech/assets/cards`
- `GET /api/fintech/assets/cards/{userId}`
- `POST /api/fintech/assets/transfer`
#### 2.3 账单管理
- **功能描述**: 用户账单记录和分析
- **核心功能**:
- 账单创建
- 账单查询
- 账单分类统计
- 账单概览
- **API接口**:
- `GET /api/fintech/bills/{userId}`
- `GET /api/fintech/bills/{userId}/type/{type}`
- `GET /api/fintech/bills/{userId}/overview`
- `POST /api/fintech/bills/create`
### 三、后台管理与权限模块
#### 3.1 管理员认证
- **功能描述**: 管理员登录和权限管理
- **核心功能**:
- 管理员登录
- 管理员创建
- 角色权限控制
- **API接口**:
- `POST /api/admin/auth/login`
- `POST /api/admin/auth/create`
#### 3.2 用户信息管理
- **功能描述**: 用户账号管理和审核
- **核心功能**:
- 用户列表查看
- 用户账号启用/禁用
- 用户审核管理
- **API接口**:
- `GET /api/admin/users`
- `GET /api/admin/users/{userId}`
- `POST /api/admin/users/{userId}/disable`
- `POST /api/admin/users/{userId}/enable`
- `GET /api/admin/users/audits/pending`
- `POST /api/admin/users/audits/{userId}/approve`
- `POST /api/admin/users/audits/{userId}/reject`
#### 3.3 折扣策略管理
- **功能描述**: 折扣策略配置和管理
- **核心功能**:
- 策略创建和编辑
- 策略启用/禁用
- 策略查询和删除
- **API接口**:
- `GET /api/admin/policies`
- `GET /api/admin/policies/active`
- `GET /api/admin/policies/{policyId}`
- `POST /api/admin/policies`
- `PUT /api/admin/policies/{policyId}`
- `POST /api/admin/policies/{policyId}/enable`
- `POST /api/admin/policies/{policyId}/disable`
- `DELETE /api/admin/policies/{policyId}`
- `GET /api/admin/policies/line/{line}`
### 四、站点管理模块
#### 4.1 站点管理
- **功能描述**: 地铁站点信息管理
- **核心功能**:
- 站点创建和编辑
- 站点启用/禁用
- 站点查询
- **API接口**:
- `GET /api/site/stations`
- `GET /api/site/stations/active`
- `GET /api/site/stations/{stationId}`
- `POST /api/site/stations`
- `PUT /api/site/stations/{stationId}`
- `POST /api/site/stations/{stationId}/enable`
- `POST /api/site/stations/{stationId}/disable`
- `GET /api/site/stations/city/{city}`
- `GET /api/site/stations/line/{line}`
#### 4.2 闸机管理
- **功能描述**: 闸机设备管理
- **核心功能**:
- 闸机创建和编辑
- 闸机启用/禁用
- 闸机状态管理
- **API接口**:
- `GET /api/site/stations/{stationId}/gates`
- `GET /api/site/gates/direction/{direction}`
- `POST /api/site/gates`
- `PUT /api/site/gates/{gateId}`
- `POST /api/site/gates/{gateId}/enable`
- `POST /api/site/gates/{gateId}/disable`
#### 4.3 闸机扫码与事件处理
- **功能描述**: 闸机扫码和事件记录
- **核心功能**:
- 进站扫码处理
- 出站扫码和扣费
- 事件记录和查询
- 错误处理
- **API接口**:
- `POST /api/site/gates/{gateCode}/entry`
- `POST /api/site/gates/{gateCode}/exit`
- `GET /api/site/gates/events/gate/{gateId}`
- `GET /api/site/gates/events/user/{userId}`
- `GET /api/site/gates/events/errors`
---
## 🔐 非功能需求
### 5.1 性能需求
- **响应时间**: API接口平均响应时间 < 200ms
- **并发支持**: 支持1000+并发用户
- **吞吐量**: 支持10000+ TPS
### 5.2 安全需求
- **认证授权**: JWT令牌认证基于角色的访问控制
- **数据加密**: 敏感数据加密存储
- **操作审计**: 完整的操作日志记录
- **输入验证**: 所有输入参数验证
### 5.3 可靠性需求
- **可用性**: 99.9%系统可用性
- **数据一致性**: 事务保证数据一致性
- **错误处理**: 完善的异常处理机制
### 5.4 可扩展性需求
- **模块化设计**: 各模块独立,便于扩展
- **数据库设计**: 支持水平扩展
- **API设计**: RESTful API便于集成
---
## 🏗️ 技术架构
### 6.1 技术栈
- **后端框架**: Spring Boot 3.4.12
- **数据库**: SQL Server
- **安全框架**: Spring Security + JWT
- **ORM框架**: Spring Data JPA
- **构建工具**: Maven
### 6.2 系统架构
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端应用 │ │ API网关 │ │ 后端服务 │
│ (Web/App) │───▶│ (Spring Boot) │───▶│ (4大模块) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐
│ 数据库 │
│ (SQL Server) │
└─────────────────┘
```
### 6.3 模块架构
```
支付宝后端系统
├── 支付宝核心模块
│ ├── 支付服务
│ ├── 收款码服务
│ ├── 出行服务
│ └── 智能助手服务
├── 金融科技模块
│ ├── 用户认证服务
│ ├── 资产管理服务
│ └── 账单管理服务
├── 后台管理模块
│ ├── 管理员认证服务
│ ├── 用户管理服务
│ └── 策略管理服务
└── 站点管理模块
├── 站点管理服务
├── 闸机管理服务
└── 事件处理服务
```
---
## 📊 数据库设计
### 7.1 数据库概述
- **数据库类型**: SQL Server
- **数据库名称**: hewendi
- **表总数**: 16个表
- **模块划分**: 4大模块
### 7.2 核心表结构
- **用户相关**: `user_account`, `fintech_users`
- **交易相关**: `payment`, `bill`
- **管理相关**: `admin_users`, `discount_policies`
- **站点相关**: `stations`, `gates`, `gate_events`
### 7.3 数据关系
- 用户与银行卡:一对多
- 站点与闸机:一对多
- 闸机与事件:一对多
- 管理员与日志:一对多
---
## 🔄 业务流程
### 8.1 支付流程
```
用户发起支付 → 验证余额 → 执行扣款 → 生成交易记录 → 返回结果
```
### 8.2 出行流程
```
用户开通出行码 → 进站扫码 → 记录进站 → 出站扫码 → 计算费用 → 扣费 → 生成出行记录
```
### 8.3 管理员操作流程
```
管理员登录 → 权限验证 → 执行操作 → 记录日志 → 返回结果
```
### 8.4 闸机扫码流程
```
用户扫码 → 验证用户状态 → 检查余额 → 记录事件 → 执行操作 → 返回结果
```
---
## 🚀 部署需求
### 9.1 环境要求
- **Java版本**: JDK 17+
- **数据库**: SQL Server 2019+
- **内存**: 最低4GB推荐8GB
- **存储**: 最低50GB根据数据量扩展
### 9.2 部署架构
```
生产环境
├── 应用服务器 (Spring Boot应用)
├── 数据库服务器 (SQL Server)
├── 负载均衡器 (可选)
└── 监控系统 (可选)
```
### 9.3 配置要求
- **端口配置**: 默认8080端口
- **数据库连接**: 配置连接池
- **安全配置**: JWT密钥配置
- **日志配置**: 日志级别和输出
---
## 📈 测试需求
### 10.1 单元测试
- 服务层单元测试覆盖率 > 80%
- 控制器层单元测试
- 工具类单元测试
### 10.2 集成测试
- API接口集成测试
- 数据库操作集成测试
- 安全认证集成测试
### 10.3 性能测试
- 压力测试
- 并发测试
- 响应时间测试
### 10.4 安全测试
- 权限控制测试
- 输入验证测试
- SQL注入防护测试
---
## 📝 文档需求
### 11.1 技术文档
- API接口文档
- 数据库设计文档
- 部署文档
- 开发环境配置文档
### 11.2 用户文档
- 管理员操作手册
- 开发者集成指南
- 故障排除指南
---
## 🔄 维护需求
### 12.1 日常维护
- 日志监控
- 性能监控
- 错误监控
- 数据库备份
### 12.2 定期维护
- 系统更新
- 安全补丁
- 性能优化
- 数据清理
---
## 🎯 验收标准
### 13.1 功能验收
- 所有API接口正常工作
- 业务流程完整无误
- 错误处理完善
- 权限控制正确
### 13.2 性能验收
- 响应时间符合要求
- 并发支持达标
- 系统稳定性良好
### 13.3 安全验收
- 认证授权机制完善
- 数据安全保护到位
- 操作审计完整
### 13.4 文档验收
- 文档完整准确
- 示例清晰易懂
- 维护指南详细
---
## 📅 项目里程碑
### 阶段一:核心功能开发
- ✅ 支付宝支付功能
- ✅ 收款码功能
- ✅ 出行功能
- ✅ 智能助手功能
### 阶段二:金融科技集成
- ✅ 用户认证系统
- ✅ 资产管理功能
- ✅ 账单管理功能
### 阶段三:后台管理开发
- ✅ 管理员权限系统
- ✅ 用户管理功能
- ✅ 策略管理功能
### 阶段四:站点管理开发
- ✅ 站点管理功能
- ✅ 闸机管理功能
- ✅ 扫码事件处理
### 阶段五:测试和部署
- 🔄 系统测试
- 🔄 性能优化
- 🔄 生产部署
---
**文档版本**: v1.0
**最后更新**: 2025/11/30
**项目状态**: 开发完成,待测试部署

@ -1,20 +0,0 @@
# simple-alipay-backend
Spring Boot 3.4.12 + JDK21 demo backend implementing simplified payment, collection QR, and travel code modules.
How to run:
- Build with Maven: `mvn clean package`
- Run: `java -jar target/simple-alipay-backend-0.0.1-SNAPSHOT.jar`
- H2 console: http://localhost:8080/h2-console (jdbc url: jdbc:h2:mem:alipay)
APIs (examples):
- POST /api/pay/execute -> {"fromUser":"alice","toMerchant":"bob","amount":"10.00","method":"balance"}
- POST /api/collect/create -> {"merchantId":"bob","validSeconds":"120"}
- POST /api/collect/refresh/{id}
- POST /api/collect/parseFromBase64 -> {"imageBase64":"<base64 png>"}
- POST /api/travel/open -> {"username":"alice","city":"Beijing","line":"Line1","payment":"balance"}
- POST /api/travel/entry -> {"username":"alice","city":"Beijing","line":"Line1"}
- POST /api/travel/exit/{recordId}
- GET /api/travel/records/{username}
Note: This project is intentionally minimal and NOT production-ready. Security and advanced error handling are omitted as requested.

@ -1,146 +0,0 @@
# 金融科技模块集成说明
## 新增功能模块
### 1. 用户认证模块
- **注册**: POST `/api/fintech/auth/register`
- **登录**: POST `/api/fintech/auth/login`
### 2. 资产管理模块
- **查询余额**: GET `/api/fintech/assets/balance/{userId}`
- **添加银行卡**: POST `/api/fintech/assets/cards`
- **查询银行卡**: GET `/api/fintech/assets/cards/{userId}`
- **转账**: POST `/api/fintech/assets/transfer`
### 3. 账单管理模块
- **查询账单**: GET `/api/fintech/bills/{userId}`
- **按类型查询**: GET `/api/fintech/bills/{userId}/type/{type}`
- **账单概览**: GET `/api/fintech/bills/{userId}/overview`
- **创建账单**: POST `/api/fintech/bills/create`
## 安全特性
- JWT令牌认证
- 密码BCrypt加密
- 无状态会话管理
## 数据库表结构
- `fintech_users` - 用户表
- `bank_card` - 银行卡表
- `bill` - 账单表
- `user_audit` - 用户审核表
## 与原项目的关系
- 完全独立的新模块,不影响原有支付宝功能
- 使用相同的数据库连接
- 共享Spring Boot框架
- 新增API路径前缀 `/api/fintech/`
## Postman 测试指南
### 1. 用户注册
**请求:**
- **方法**: POST
- **URL**: `http://localhost:8080/api/fintech/auth/register`
- **Headers**: `Content-Type: application/json`
- **Body**:
```json
{
"username": "testuser",
"password": "123456",
"phone": "13800138000"
}
```
**响应示例:**
```json
{
"message": "注册成功",
"userId": 1,
"username": "testuser"
}
```
### 2. 用户登录
**请求:**
- **方法**: POST
- **URL**: `http://localhost:8080/api/fintech/auth/login`
- **Headers**: `Content-Type: application/json`
- **Body**:
```json
{
"username": "testuser",
"password": "123456"
}
```
**响应示例:**
```json
{
"message": "登录成功",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"userId": 1,
"username": "testuser",
"balance": 0.00
}
```
### 3. 查询余额 (需要认证)
**请求:**
- **方法**: GET
- **URL**: `http://localhost:8080/api/fintech/assets/balance/1`
- **Headers**:
- `Content-Type: application/json`
- `Authorization: Bearer <your-jwt-token>`
**响应示例:**
```json
{
"balance": 0.00
}
```
### 4. 添加银行卡
**请求:**
- **方法**: POST
- **URL**: `http://localhost:8080/api/fintech/assets/cards`
- **Headers**:
- `Content-Type: application/json`
- `Authorization: Bearer <your-jwt-token>`
- **Body**:
```json
{
"userId": 1,
"bankName": "中国银行",
"cardNumber": "6222021234567890123",
"isDefault": true
}
```
### 5. 查询账单
**请求:**
- **方法**: GET
- **URL**: `http://localhost:8080/api/fintech/bills/1`
- **Headers**:
- `Content-Type: application/json`
- `Authorization: Bearer <your-jwt-token>`
### 6. 转账操作
**请求:**
- **方法**: POST
- **URL**: `http://localhost:8080/api/fintech/assets/transfer`
- **Headers**:
- `Content-Type: application/json`
- `Authorization: Bearer <your-jwt-token>`
- **Body**:
```json
{
"fromUserId": 1,
"toUserId": 2,
"amount": 100.00
}
```
## 注意事项
- 金融科技模块需要认证才能访问资产和账单相关API
- 认证API公开访问
- 与原支付宝模块完全解耦,可以独立使用

File diff suppressed because it is too large Load Diff

@ -1,21 +0,0 @@
#!/bin/sh
set -e
MAVEN_PROJECTBASEDIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)"
WRAPPER_DIR="$MAVEN_PROJECTBASEDIR/.mvn/wrapper"
WRAPPER_JAR="$WRAPPER_DIR/maven-wrapper.jar"
WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
if [ ! -f "$WRAPPER_JAR" ]; then
mkdir -p "$WRAPPER_DIR"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$WRAPPER_URL" -o "$WRAPPER_JAR"
elif command -v wget >/dev/null 2>&1; then
wget -q "$WRAPPER_URL" -O "$WRAPPER_JAR"
else
echo "Neither curl nor wget is available to download Maven Wrapper." 1>&2
exit 1
fi
fi
exec java -Dmaven.multiModuleProjectDirectory="$MAVEN_PROJECTBASEDIR" -classpath "$WRAPPER_JAR" org.apache.maven.wrapper.MavenWrapperMain "$@"

@ -1,17 +0,0 @@
@ECHO OFF
SETLOCAL
set "MAVEN_PROJECTBASEDIR=%~dp0"
if "%MAVEN_PROJECTBASEDIR:~-1%"=="\" set "MAVEN_PROJECTBASEDIR=%MAVEN_PROJECTBASEDIR:~0,-1%"
set "WRAPPER_DIR=%MAVEN_PROJECTBASEDIR%\.mvn\wrapper"
set "WRAPPER_JAR=%WRAPPER_DIR%\maven-wrapper.jar"
set "WRAPPER_URL=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
if not exist "%WRAPPER_JAR%" (
if not exist "%WRAPPER_DIR%" mkdir "%WRAPPER_DIR%"
powershell -NoProfile -ExecutionPolicy Bypass -Command "try { Invoke-WebRequest -UseBasicParsing -Uri '%WRAPPER_URL%' -OutFile '%WRAPPER_JAR%' } catch { Write-Error $_; exit 1 }"
if errorlevel 1 exit /b 1
)
java -Dmaven.multiModuleProjectDirectory="%MAVEN_PROJECTBASEDIR%" -classpath "%WRAPPER_JAR%" org.apache.maven.wrapper.MavenWrapperMain %*

@ -1,129 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>simple-alipay-backend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.12</version>
<relativePath/>
</parent>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- SQL Server JDBC 驱动 -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!-- ZXing 二维码生成与解析 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.5.3</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JSON 时间处理 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- Bean 校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -1,11 +0,0 @@
package com.example.alipay;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SimpleAlipayBackendApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleAlipayBackendApplication.class, args);
}
}

@ -1,55 +0,0 @@
package com.example.alipay.config;
import com.example.alipay.model.UserAccount;
import com.example.alipay.model.fintech.User;
import com.example.alipay.model.fintech.UserStatus;
import com.example.alipay.repository.UserAccountRepository;
import com.example.alipay.repository.fintech.UserRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.math.BigDecimal;
@Configuration
public class DataLoader {
@Bean
CommandLineRunner init(UserAccountRepository repo){
return args -> {
if (repo.findByUsername("alice").isEmpty()){
UserAccount u = new UserAccount();
u.setUsername("alice");
u.setDisplayName("Alice");
u.setDefaultPaymentMethod("balance");
repo.save(u);
}
if (repo.findByUsername("bob").isEmpty()){
UserAccount u2 = new UserAccount();
u2.setUsername("bob");
u2.setDisplayName("Bob Merchant");
u2.setDefaultPaymentMethod("balance");
repo.save(u2);
}
};
}
@Bean
CommandLineRunner initFintech(UserRepository userRepository, PasswordEncoder encoder){
return args -> {
if (!userRepository.existsByUsername("demo_payer")) {
User user = User.builder()
.username("demo_payer")
.password(encoder.encode("demo123"))
.phone("13800000000")
.balance(new BigDecimal("100000000.00"))
.status(UserStatus.ACTIVE)
.build();
user.setPayPassword(encoder.encode("123456"));
user.setPayPwdFailedAttempts(0);
user.setPayPwdLockedUntil(null);
userRepository.save(user);
}
};
}
}

@ -1,98 +0,0 @@
package com.example.alipay.config;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.http.converter.HttpMessageNotReadableException;
import java.time.Instant;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GlobalExceptionHandler.class);
private Map<String, Object> body(HttpStatus status, String error, String message, String exception, String path) {
return Map.of(
"status", status.value(),
"error", error,
"message", message,
"exception", exception,
"path", path,
"timestamp", Instant.now().toString()
);
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<?> handleNotReadable(HttpMessageNotReadableException e, HttpServletRequest request) {
log.warn("invalid_request: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body(HttpStatus.BAD_REQUEST, "invalid_request", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(org.springframework.http.converter.HttpMessageNotWritableException.class)
public ResponseEntity<?> handleNotWritable(org.springframework.http.converter.HttpMessageNotWritableException e, HttpServletRequest request) {
log.error("message_not_writable", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body(HttpStatus.INTERNAL_SERVER_ERROR, "message_not_writable", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidation(MethodArgumentNotValidException e, HttpServletRequest request) {
log.warn("validation_failed: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body(HttpStatus.BAD_REQUEST, "validation_failed", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<?> handleIllegalArgument(IllegalArgumentException e, HttpServletRequest request) {
log.warn("invalid_argument: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body(HttpStatus.BAD_REQUEST, "invalid_argument", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<?> handleAccessDenied(AccessDeniedException e, HttpServletRequest request) {
log.warn("forbidden: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(body(HttpStatus.FORBIDDEN, "forbidden", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler({BadCredentialsException.class, UsernameNotFoundException.class})
public ResponseEntity<?> handleAuth(Exception e, HttpServletRequest request) {
log.warn("unauthorized: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(body(HttpStatus.UNAUTHORIZED, "unauthorized", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(DataAccessException.class)
public ResponseEntity<?> handleDataAccess(DataAccessException e, HttpServletRequest request) {
log.error("data_access_error", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body(HttpStatus.INTERNAL_SERVER_ERROR, "data_access_error", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<?> handleNpe(NullPointerException e, HttpServletRequest request) {
log.error("null_pointer", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body(HttpStatus.INTERNAL_SERVER_ERROR, "null_pointer", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleGeneric(Exception e, HttpServletRequest request) {
log.error("internal_error", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body(HttpStatus.INTERNAL_SERVER_ERROR, "internal_error", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(org.springframework.web.servlet.NoHandlerFoundException.class)
public ResponseEntity<?> handleNoHandler(org.springframework.web.servlet.NoHandlerFoundException e, HttpServletRequest request) {
log.warn("not_found: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body(HttpStatus.NOT_FOUND, "not_found", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
@ExceptionHandler(org.springframework.web.servlet.resource.NoResourceFoundException.class)
public ResponseEntity<?> handleNoResource(org.springframework.web.servlet.resource.NoResourceFoundException e, HttpServletRequest request) {
log.warn("not_found: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body(HttpStatus.NOT_FOUND, "not_found", e.getMessage(), e.getClass().getSimpleName(), request.getRequestURI()));
}
}

@ -1,92 +0,0 @@
package com.example.alipay.config.fintech;
import com.example.alipay.security.fintech.CustomUserDetailsService;
import com.example.alipay.security.fintech.JwtAuthenticationFilter;
import com.example.alipay.security.fintech.JwtUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final CustomUserDetailsService userDetailsService;
private final JwtUtil jwtUtil;
public SecurityConfig(CustomUserDetailsService userDetailsService, JwtUtil jwtUtil) {
this.userDetailsService = userDetailsService;
this.jwtUtil = jwtUtil;
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter(userDetailsService, jwtUtil);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authz -> authz
// 允许公开访问的API
.requestMatchers("/api/fintech/auth/register", "/api/fintech/auth/login").permitAll()
.requestMatchers("/api/fintech/auth/pay-password/**").authenticated()
.requestMatchers("/api/admin/auth/**").permitAll()
.requestMatchers("/api/pay/**").permitAll()
.requestMatchers("/api/collect/**").permitAll()
.requestMatchers("/api/travel/**").permitAll()
.requestMatchers("/api/assistant/**").permitAll()
.requestMatchers("/error").permitAll()
.requestMatchers("/api/site/gates/*/entry").permitAll()
.requestMatchers("/api/site/gates/*/exit").permitAll()
.requestMatchers(HttpMethod.GET, "/api/site/stations/**").permitAll()
// Actuator 健康检查端点
.requestMatchers("/actuator/health").permitAll()
// 管理员API需要SUPER_ADMIN角色
.requestMatchers("/api/admin/**").hasRole("SUPER_ADMIN")
// 站点管理API需要SITE_MANAGER角色
.requestMatchers("/api/site/**").hasAnyRole("SITE_MANAGER", "SUPER_ADMIN")
// 金融科技API需要认证
.requestMatchers("/api/fintech/assets/**").authenticated()
.requestMatchers("/api/fintech/bills/**").authenticated()
.anyRequest().authenticated()
)
.exceptionHandling(ex -> ex
.authenticationEntryPoint((request, response, authException) -> {
response.setStatus(401);
response.setContentType("application/json;charset=UTF-8");
String json = "{\"status\":401,\"error\":\"unauthorized\",\"message\":\"Authentication required\",\"path\":\"" + request.getRequestURI() + "\"}";
response.getWriter().write(json);
})
.accessDeniedHandler((request, response, accessDeniedException) -> {
response.setStatus(403);
response.setContentType("application/json;charset=UTF-8");
String json = "{\"status\":403,\"error\":\"forbidden\",\"message\":\"Access denied\",\"path\":\"" + request.getRequestURI() + "\"}";
response.getWriter().write(json);
})
)
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}

@ -1,46 +0,0 @@
package com.example.alipay.controller;
import com.example.alipay.model.CollectionQRCode;
import com.example.alipay.service.CollectionService;
import com.example.alipay.service.QrService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Base64;
import java.util.Map;
@RestController
@RequestMapping("/api/collect")
public class CollectionController {
private final CollectionService collectionService;
private final QrService qrService;
public CollectionController(CollectionService collectionService, QrService qrService){
this.collectionService = collectionService;
this.qrService = qrService;
}
@PostMapping("/create")
public ResponseEntity<?> create(@RequestBody Map<String,String> body) throws Exception {
String merchantId = body.getOrDefault("merchantId","m1");
int validSeconds = Integer.parseInt(body.getOrDefault("validSeconds","120"));
CollectionQRCode q = collectionService.generateInitialQRCode(merchantId, validSeconds);
String base64 = qrService.generateQrBase64(q.getContent(),300,300);
return ResponseEntity.ok(Map.of("qrBase64", base64, "id", q.getId(), "expireAt", q.getExpireAt().toString()));
}
@PostMapping("/refresh/{id}")
public ResponseEntity<?> refresh(@PathVariable Long id, @RequestBody Map<String,String> body) throws Exception {
int validSeconds = Integer.parseInt(body.getOrDefault("validSeconds","120"));
CollectionQRCode q = collectionService.refreshQRCode(id, validSeconds);
String base64 = qrService.generateQrBase64(q.getContent(),300,300);
return ResponseEntity.ok(Map.of("qrBase64", base64, "id", q.getId(), "expireAt", q.getExpireAt().toString()));
}
@PostMapping("/parseFromBase64")
public ResponseEntity<?> parseFromBase64(@RequestBody Map<String,String> body) throws Exception {
String b64 = body.get("imageBase64");
byte[] bytes = Base64.getDecoder().decode(b64);
String text = qrService.parseQrFromBytes(bytes);
return ResponseEntity.ok(Map.of("text", text));
}
}

@ -1,75 +0,0 @@
package com.example.alipay.controller;
import com.example.alipay.model.Payment;
import com.example.alipay.service.PaymentService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.Map;
@RestController
@RequestMapping("/api/pay")
public class PaymentController {
private final PaymentService paymentService;
public PaymentController(PaymentService paymentService){this.paymentService = paymentService;}
@PostMapping("/execute")
public ResponseEntity<?> executePayment(@RequestBody Map<String,String> body){
String from = body.getOrDefault("fromUser","alice");
String to = body.getOrDefault("toMerchant","merchant1");
String amtS = body.getOrDefault("amount","0.00");
String method = body.getOrDefault("method","qr");
String paySource = body.get("paySource");
String payCardIdS = body.get("payCardId");
Long payCardId = (payCardIdS == null || payCardIdS.isBlank()) ? null : Long.valueOf(payCardIdS);
String receiveSource = body.get("receiveSource");
String receiveCardIdS = body.get("receiveCardId");
Long receiveCardId = (receiveCardIdS == null || receiveCardIdS.isBlank()) ? null : Long.valueOf(receiveCardIdS);
String payPassword = body.get("payPassword");
BigDecimal amount = new BigDecimal(amtS);
String normalizedFrom = normalizeUserKey(from);
String normalizedTo = normalizeUserKey(to);
if (normalizedFrom.equals(normalizedTo)) {
return ResponseEntity.status(422).body(Map.of("error", "BUSINESS_RULE"));
}
if ("balance".equalsIgnoreCase(paySource) || "card".equalsIgnoreCase(paySource)) {
String pre = paymentService.preCheck(from, payPassword);
if (pre != null) {
if ("USER_NOT_FOUND".equals(pre)) {
return ResponseEntity.status(404).body(Map.of("error", pre));
}
return ResponseEntity.status(403).body(Map.of("error", pre));
}
}
Payment p = paymentService.createAndExecutePayment(from,to,amount,method,paySource,payCardId,receiveSource,receiveCardId);
if ("FAILED".equals(p.getStatus())) {
String code = p.getErrorCode();
if ("INSUFFICIENT_FUNDS".equals(code)) {
return ResponseEntity.status(402).body(p);
} else if ("CARD_NOT_FOUND".equals(code) || "CARD_OWNERSHIP_ERROR".equals(code)) {
return ResponseEntity.status(404).body(p);
} else if ("INVALID_PAY_SOURCE".equals(code)) {
return ResponseEntity.status(422).body(p);
} else if ("RECEIVER_NOT_FOUND".equals(code)) {
return ResponseEntity.status(404).body(p);
}
}
return ResponseEntity.ok(p);
}
private String normalizeUserKey(String raw) {
if (raw == null) return "";
String v = raw.trim();
if (v.startsWith("collect://")) {
v = v.substring("collect://".length());
}
int slash = v.indexOf('/');
if (slash > 0) {
return v.substring(0, slash);
}
return v;
}
}

@ -1,37 +0,0 @@
package com.example.alipay.controller;
import com.example.alipay.model.AssistantMessage;
import com.example.alipay.service.SmartAssistantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/assistant")
public class SmartAssistantController {
@Autowired
private SmartAssistantService assistantService;
@GetMapping("/welcome")
public String welcome() {
return assistantService.getWelcomeMessage();
}
@PostMapping("/chat")
public AssistantMessage chat(@RequestParam String username, @RequestParam String content) {
return assistantService.processChat(username, content);
}
@GetMapping("/analysis/monthly")
public Map<String, Object> getMonthlyAnalysis(@RequestParam String username) {
return assistantService.analyzeMonthly(username);
}
@GetMapping("/analysis/alerts")
public List<?> getAbnormalRecords(@RequestParam String username) {
return assistantService.getLargePayments(username);
}
}

@ -1,48 +0,0 @@
package com.example.alipay.controller;
import com.example.alipay.model.TravelPass;
import com.example.alipay.model.TravelRecord;
import com.example.alipay.service.TravelService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/travel")
public class TravelController {
private final TravelService travelService;
public TravelController(TravelService travelService){this.travelService = travelService;}
@PostMapping("/open")
public ResponseEntity<?> open(@RequestBody Map<String,String> body){
String username = body.getOrDefault("username","alice");
String city = body.getOrDefault("city","Beijing");
String line = body.getOrDefault("line","Line1");
String payment = body.getOrDefault("payment","balance");
TravelPass p = travelService.openTravelPass(username,city,line,payment);
return ResponseEntity.ok(p);
}
@PostMapping("/entry")
public ResponseEntity<?> entry(@RequestBody Map<String,String> body){
String username = body.getOrDefault("username","alice");
String city = body.getOrDefault("city","Beijing");
String line = body.getOrDefault("line","Line1");
TravelRecord r = travelService.entry(username,city,line);
return ResponseEntity.ok(r);
}
@PostMapping("/exit/{recordId}")
public ResponseEntity<?> exit(@PathVariable Long recordId){
TravelRecord r = travelService.exit(recordId);
return ResponseEntity.ok(r);
}
@GetMapping("/records/{username}")
public ResponseEntity<?> records(@PathVariable String username){
List<TravelRecord> list = travelService.listRecords(username);
return ResponseEntity.ok(list);
}
}

@ -1,95 +0,0 @@
package com.example.alipay.controller.admin;
import com.example.alipay.model.admin.AdminUser;
import com.example.alipay.model.admin.CreateAdminRequest;
import com.example.alipay.security.fintech.JwtUtil;
import com.example.alipay.service.admin.AdminAuthService;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@RestController
@RequestMapping("/api/admin/auth")
public class AdminAuthController {
private final AdminAuthService adminAuthService;
private final JwtUtil jwtUtil;
public AdminAuthController(AdminAuthService adminAuthService, JwtUtil jwtUtil) {
this.adminAuthService = adminAuthService;
this.jwtUtil = jwtUtil;
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Map<String, String> request) {
return loginInternal(request.get("username"), request.get("password"));
}
@PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<?> loginForm(@RequestParam Map<String, String> request) {
return loginInternal(request.get("username"), request.get("password"));
}
private ResponseEntity<?> loginInternal(String username, String password) {
if (username == null || password == null) {
return ResponseEntity.badRequest().body(Map.of("error", "用户名和密码不能为空"));
}
Optional<AdminUser> adminOpt = adminAuthService.authenticate(username, password);
if (adminOpt.isPresent()) {
AdminUser admin = adminOpt.get();
String token = jwtUtil.generateToken(username);
return ResponseEntity.ok(Map.of(
"message", "管理员登录成功",
"token", token,
"adminId", admin.getId(),
"username", admin.getUsername(),
"role", admin.getRole()
));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "用户名或密码错误"));
}
}
@PostMapping("/create")
public ResponseEntity<?> createAdmin(@RequestBody CreateAdminRequest request) {
try {
String username = request.getUsername();
String password = request.getPassword();
String role = request.getRole();
if (username == null || password == null || role == null) {
return ResponseEntity.badRequest().body(Map.of("error", "用户名、密码和角色不能为空"));
}
AdminUser admin = adminAuthService.createAdmin(username, password, role);
return ResponseEntity.ok(Map.of(
"message", "管理员创建成功",
"adminId", admin.getId(),
"username", admin.getUsername(),
"role", admin.getRole()
));
} catch (RuntimeException e) {
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
}
}
@GetMapping("/admins")
public ResponseEntity<?> listAdmins() {
List<AdminUser> admins = adminAuthService.getAllAdmins();
return ResponseEntity.ok(admins);
}
@DeleteMapping("/admins/{adminId}")
public ResponseEntity<?> deleteAdmin(@PathVariable Long adminId) {
boolean success = adminAuthService.deleteAdmin(adminId);
if (success) {
return ResponseEntity.ok(Map.of("message", "管理员已删除", "adminId", adminId));
}
return ResponseEntity.badRequest().body(Map.of("error", "管理员不存在", "adminId", adminId));
}
}

@ -1,36 +0,0 @@
package com.example.alipay.controller.admin;
import com.example.alipay.model.admin.AdminOperationLog;
import com.example.alipay.service.admin.AdminOperationLogService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/admin/logs")
public class AdminLogController {
private final AdminOperationLogService logService;
public AdminLogController(AdminOperationLogService logService) {
this.logService = logService;
}
@GetMapping
public ResponseEntity<?> getAllLogs() {
List<AdminOperationLog> logs = logService.getAllLogs();
return ResponseEntity.ok(logs);
}
@GetMapping("/admin/{adminId}")
public ResponseEntity<?> getLogsByAdmin(@PathVariable Long adminId) {
List<AdminOperationLog> logs = logService.getLogsByAdmin(adminId);
return ResponseEntity.ok(logs);
}
@GetMapping("/type/{operationType}")
public ResponseEntity<?> getLogsByType(@PathVariable String operationType) {
List<AdminOperationLog> logs = logService.getLogsByType(operationType);
return ResponseEntity.ok(logs);
}
}

@ -1,100 +0,0 @@
package com.example.alipay.controller.admin;
import com.example.alipay.model.admin.DiscountPolicy;
import com.example.alipay.service.admin.DiscountPolicyService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/admin/policies")
public class DiscountPolicyController {
private final DiscountPolicyService discountPolicyService;
public DiscountPolicyController(DiscountPolicyService discountPolicyService) {
this.discountPolicyService = discountPolicyService;
}
@GetMapping
public ResponseEntity<?> getAllPolicies() {
List<DiscountPolicy> policies = discountPolicyService.getAllPolicies();
return ResponseEntity.ok(policies);
}
@GetMapping("/active")
public ResponseEntity<?> getActivePolicies() {
List<DiscountPolicy> policies = discountPolicyService.getActivePolicies();
return ResponseEntity.ok(policies);
}
@GetMapping("/{policyId}")
public ResponseEntity<?> getPolicyById(@PathVariable Long policyId) {
return discountPolicyService.getPolicyById(policyId)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<?> createPolicy(@RequestBody DiscountPolicy policy) {
try {
DiscountPolicy createdPolicy = discountPolicyService.createPolicy(policy);
return ResponseEntity.ok(Map.of(
"message", "折扣策略创建成功",
"policyId", createdPolicy.getId()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
}
}
@PutMapping("/{policyId}")
public ResponseEntity<?> updatePolicy(@PathVariable Long policyId, @RequestBody DiscountPolicy policyDetails) {
DiscountPolicy updatedPolicy = discountPolicyService.updatePolicy(policyId, policyDetails);
if (updatedPolicy != null) {
return ResponseEntity.ok(Map.of(
"message", "折扣策略更新成功",
"policyId", updatedPolicy.getId()
));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "策略不存在"));
}
}
@PostMapping("/{policyId}/enable")
public ResponseEntity<?> enablePolicy(@PathVariable Long policyId) {
boolean success = discountPolicyService.enablePolicy(policyId);
if (success) {
return ResponseEntity.ok(Map.of("message", "折扣策略已启用"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "策略不存在"));
}
}
@PostMapping("/{policyId}/disable")
public ResponseEntity<?> disablePolicy(@PathVariable Long policyId) {
boolean success = discountPolicyService.disablePolicy(policyId);
if (success) {
return ResponseEntity.ok(Map.of("message", "折扣策略已禁用"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "策略不存在"));
}
}
@DeleteMapping("/{policyId}")
public ResponseEntity<?> deletePolicy(@PathVariable Long policyId) {
boolean success = discountPolicyService.deletePolicy(policyId);
if (success) {
return ResponseEntity.ok(Map.of("message", "折扣策略已删除"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "策略不存在"));
}
}
@GetMapping("/line/{line}")
public ResponseEntity<?> getPoliciesByLine(@PathVariable String line) {
List<DiscountPolicy> policies = discountPolicyService.getPoliciesByLine(line);
return ResponseEntity.ok(policies);
}
}

@ -1,84 +0,0 @@
package com.example.alipay.controller.admin;
import com.example.alipay.model.fintech.User;
import com.example.alipay.model.fintech.UserAudit;
import com.example.alipay.service.admin.UserManagementService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/admin/users")
public class UserManagementController {
private final UserManagementService userManagementService;
public UserManagementController(UserManagementService userManagementService) {
this.userManagementService = userManagementService;
}
@GetMapping
public ResponseEntity<?> getAllUsers() {
List<User> users = userManagementService.getAllUsers();
return ResponseEntity.ok(users);
}
@GetMapping("/{userId}")
public ResponseEntity<?> getUserById(@PathVariable Long userId) {
return userManagementService.getUserById(userId)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping("/{userId}/disable")
public ResponseEntity<?> disableUser(@PathVariable Long userId) {
boolean success = userManagementService.disableUser(userId);
if (success) {
return ResponseEntity.ok(Map.of("message", "用户已禁用"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "用户不存在"));
}
}
@PostMapping("/{userId}/enable")
public ResponseEntity<?> enableUser(@PathVariable Long userId) {
boolean success = userManagementService.enableUser(userId);
if (success) {
return ResponseEntity.ok(Map.of("message", "用户已启用"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "用户不存在"));
}
}
@GetMapping("/audits/pending")
public ResponseEntity<?> getPendingAudits() {
List<UserAudit> audits = userManagementService.getPendingAudits();
return ResponseEntity.ok(audits);
}
@PostMapping("/audits/{userId}/approve")
public ResponseEntity<?> approveUserAudit(@PathVariable Long userId) {
boolean success = userManagementService.approveUserAudit(userId);
if (success) {
return ResponseEntity.ok(Map.of("message", "用户审核已通过"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "审核记录不存在"));
}
}
@PostMapping("/audits/{userId}/reject")
public ResponseEntity<?> rejectUserAudit(@PathVariable Long userId, @RequestBody Map<String, String> request) {
String rejectReason = request.get("rejectReason");
if (rejectReason == null || rejectReason.trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "驳回原因不能为空"));
}
boolean success = userManagementService.rejectUserAudit(userId, rejectReason);
if (success) {
return ResponseEntity.ok(Map.of("message", "用户审核已驳回"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "审核记录不存在"));
}
}
}

@ -1,72 +0,0 @@
package com.example.alipay.controller.fintech;
import com.example.alipay.model.fintech.BankCard;
import com.example.alipay.service.fintech.AssetService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/fintech/assets")
public class AssetController {
private final AssetService assetService;
public AssetController(AssetService assetService) {
this.assetService = assetService;
}
@GetMapping("/balance/{userId}")
public ResponseEntity<?> getBalance(@PathVariable Long userId) {
BigDecimal balance = assetService.getBalance(userId);
return ResponseEntity.ok(Map.of("balance", balance));
}
@PostMapping("/cards")
public ResponseEntity<?> addBankCard(@RequestBody Map<String, Object> request) {
try {
Long userId = Long.valueOf(request.get("userId").toString());
String bankName = (String) request.get("bankName");
String cardNumber = (String) request.get("cardNumber");
Boolean isDefault = (Boolean) request.get("isDefault");
if (isDefault == null) {
isDefault = false;
}
BankCard card = assetService.addBankCard(userId, bankName, cardNumber, isDefault);
return ResponseEntity.ok(Map.of(
"message", "银行卡添加成功",
"cardId", card.getId()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
}
}
@GetMapping("/cards/{userId}")
public ResponseEntity<?> getUserCards(@PathVariable Long userId) {
List<BankCard> cards = assetService.getUserCards(userId);
return ResponseEntity.ok(cards);
}
@PostMapping("/transfer")
public ResponseEntity<?> transfer(@RequestBody Map<String, Object> request) {
try {
Long fromUserId = Long.valueOf(request.get("fromUserId").toString());
Long toUserId = Long.valueOf(request.get("toUserId").toString());
BigDecimal amount = new BigDecimal(request.get("amount").toString());
boolean success = assetService.transfer(fromUserId, toUserId, amount);
if (success) {
return ResponseEntity.ok(Map.of("message", "转账成功"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "转账失败,余额不足或用户不存在"));
}
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
}
}
}

@ -1,139 +0,0 @@
package com.example.alipay.controller.fintech;
import com.example.alipay.model.fintech.User;
import com.example.alipay.security.fintech.JwtUtil;
import com.example.alipay.repository.fintech.UserAuditRepository;
import com.example.alipay.service.fintech.AuthService;
import com.example.alipay.service.admin.AdminOperationLogger;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.Optional;
@RestController
@RequestMapping("/api/fintech/auth")
public class AuthController {
private final AuthService authService;
private final JwtUtil jwtUtil;
private final UserAuditRepository userAuditRepository;
private final AdminOperationLogger adminOperationLogger;
public AuthController(AuthService authService, JwtUtil jwtUtil, UserAuditRepository userAuditRepository, AdminOperationLogger adminOperationLogger) {
this.authService = authService;
this.jwtUtil = jwtUtil;
this.userAuditRepository = userAuditRepository;
this.adminOperationLogger = adminOperationLogger;
}
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody Map<String, String> request) {
try {
String username = request.get("username");
String password = request.get("password");
String phone = request.get("phone");
String realName = request.get("realName");
String idCardNumber = request.get("idCardNumber");
if (username == null || password == null) {
return ResponseEntity.badRequest().body(Map.of("error", "用户名和密码不能为空"));
}
User user = authService.register(username, password, phone, realName, idCardNumber);
return ResponseEntity.ok(Map.of(
"message", "注册成功",
"userId", user.getId(),
"username", user.getUsername()
));
} catch (RuntimeException e) {
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
}
}
@PostMapping(value = "/register", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<?> registerForm(@RequestParam Map<String, String> request) {
return register(request);
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Map<String, String> request) {
return loginInternal(request.get("username"), request.get("password"));
}
@PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<?> loginForm(@RequestParam Map<String, String> request) {
return loginInternal(request.get("username"), request.get("password"));
}
private ResponseEntity<?> loginInternal(String username, String password) {
if (username == null || password == null) {
return ResponseEntity.badRequest().body(Map.of("error", "用户名和密码不能为空"));
}
Optional<User> userOpt = authService.authenticate(username, password);
if (userOpt.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "用户名或密码错误"));
}
User user = userOpt.get();
switch (user.getStatus()) {
case ACTIVE -> {
String token = jwtUtil.generateToken(username);
return ResponseEntity.ok(Map.of(
"message", "登录成功",
"token", token,
"userId", user.getId(),
"username", user.getUsername(),
"balance", user.getBalance()
));
}
case PENDING_AUDIT -> {
return ResponseEntity.status(403).body(Map.of(
"error", "账户待审核,审核通过后方可登录"
));
}
case REJECTED -> {
String reason = userAuditRepository.findByUserId(user.getId())
.map(a -> a.getRejectReason())
.orElse("审核已被驳回,请重新提交或联系管理员");
return ResponseEntity.status(403).body(Map.of(
"error", reason
));
}
case INACTIVE -> {
return ResponseEntity.status(403).body(Map.of(
"error", "账户已禁用,请联系管理员"
));
}
default -> {
return ResponseEntity.status(403).body(Map.of("error", "账户状态异常"));
}
}
}
@PostMapping("/pay-password/set")
public ResponseEntity<?> setPayPassword(@RequestHeader(value = "Authorization", required = false) String auth,
@RequestBody Map<String, String> request) {
if (auth == null || !auth.startsWith("Bearer ")) {
return ResponseEntity.status(401).body(Map.of("error", "unauthorized"));
}
String token = auth.substring(7);
String requester = jwtUtil.extractUsername(token);
String userIdS = request.get("userId");
String newPassword = request.get("newPassword");
String oldPassword = request.get("oldPassword");
if (userIdS == null || newPassword == null || newPassword.isBlank()) {
return ResponseEntity.badRequest().body(Map.of("error", "invalid_request"));
}
Long userId = Long.valueOf(userIdS);
boolean ok = authService.setPayPassword(userId, newPassword, oldPassword, requester);
adminOperationLogger.log("USER_SET_PAY_PASSWORD", "USER", userId, "set pay password", ok, ok ? null : "forbidden");
if (ok) {
return ResponseEntity.ok(Map.of("success", true, "message", "更新成功"));
} else {
return ResponseEntity.status(403).body(Map.of("error", "forbidden"));
}
}
}

@ -1,57 +0,0 @@
package com.example.alipay.controller.fintech;
import com.example.alipay.model.fintech.Bill;
import com.example.alipay.service.fintech.BillService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/fintech/bills")
public class BillController {
private final BillService billService;
public BillController(BillService billService) {
this.billService = billService;
}
@GetMapping("/{userId}")
public ResponseEntity<?> getUserBills(@PathVariable Long userId) {
List<Bill> bills = billService.getUserBills(userId);
return ResponseEntity.ok(bills);
}
@GetMapping("/{userId}/type/{type}")
public ResponseEntity<?> getUserBillsByType(@PathVariable Long userId, @PathVariable String type) {
List<Bill> bills = billService.getUserBillsByType(userId, type);
return ResponseEntity.ok(bills);
}
@GetMapping("/{userId}/overview")
public ResponseEntity<?> getBillOverview(@PathVariable Long userId) {
BillService.BillOverviewDto overview = billService.getBillOverview(userId);
return ResponseEntity.ok(overview);
}
@PostMapping("/create")
public ResponseEntity<?> createBill(@RequestBody Map<String, Object> request) {
try {
Long userId = Long.valueOf(request.get("userId").toString());
BigDecimal amount = new BigDecimal(request.get("amount").toString());
String type = (String) request.get("type");
String category = (String) request.get("category");
String remark = (String) request.get("remark");
Bill bill = billService.createBill(userId, amount, type, category, remark);
return ResponseEntity.ok(Map.of(
"message", "账单创建成功",
"billId", bill.getId()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
}
}
}

@ -1,108 +0,0 @@
package com.example.alipay.controller.site;
import com.example.alipay.model.site.GateEvent;
import com.example.alipay.service.site.GateEventService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/site/gates")
public class GateEventController {
private final GateEventService gateEventService;
public GateEventController(GateEventService gateEventService) {
this.gateEventService = gateEventService;
}
@PostMapping("/{gateCode}/entry")
public ResponseEntity<?> processEntry(@PathVariable String gateCode, @RequestBody Map<String, String> request) {
String qrCode = request.get("qrCode");
if (qrCode == null || qrCode.trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "二维码不能为空"));
}
Long stationId = parseStationId(request.get("stationId"));
GateEvent event = gateEventService.processEntry(gateCode, qrCode, stationId);
if ("SUCCESS".equals(event.getStatus())) {
return ResponseEntity.ok(Map.of(
"message", "进站成功",
"eventId", event.getId(),
"transactionId", event.getTransactionId(),
"userId", event.getUserId(),
"username", event.getUsername()
));
} else {
return ResponseEntity.badRequest().body(Map.of(
"error", event.getErrorMessage(),
"errorCode", event.getErrorCode(),
"eventId", event.getId()
));
}
}
@PostMapping("/{gateCode}/exit")
public ResponseEntity<?> processExit(@PathVariable String gateCode, @RequestBody Map<String, String> request) {
String qrCode = request.get("qrCode");
if (qrCode == null || qrCode.trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "二维码不能为空"));
}
Long stationId = parseStationId(request.get("stationId"));
GateEvent event = gateEventService.processExit(gateCode, qrCode, stationId);
if ("SUCCESS".equals(event.getStatus())) {
return ResponseEntity.ok(Map.of(
"message", "出站成功",
"eventId", event.getId(),
"transactionId", event.getTransactionId(),
"userId", event.getUserId(),
"username", event.getUsername(),
"fare", event.getFare(),
"remark", event.getRemark()
));
} else {
return ResponseEntity.badRequest().body(Map.of(
"error", event.getErrorMessage(),
"errorCode", event.getErrorCode(),
"eventId", event.getId()
));
}
}
@GetMapping("/events/gate/{gateId}")
public ResponseEntity<?> getGateEvents(@PathVariable Long gateId) {
List<GateEvent> events = gateEventService.getGateEvents(gateId);
return ResponseEntity.ok(events);
}
@GetMapping("/events/user/{userId}")
public ResponseEntity<?> getUserEvents(@PathVariable Long userId) {
List<GateEvent> events = gateEventService.getUserEvents(userId);
return ResponseEntity.ok(events);
}
@GetMapping("/events/errors")
public ResponseEntity<?> getErrorEvents() {
List<GateEvent> events = gateEventService.getErrorEvents();
return ResponseEntity.ok(events);
}
@GetMapping("/events/{eventId}")
public ResponseEntity<?> getEventById(@PathVariable Long eventId) {
// 这里需要添加获取单个事件的方法
return ResponseEntity.ok(Map.of("message", "获取事件详情功能待实现"));
}
private Long parseStationId(String stationId) {
if (stationId == null || stationId.isBlank()) return null;
try {
return Long.parseLong(stationId.trim());
} catch (NumberFormatException e) {
return null;
}
}
}

@ -1,172 +0,0 @@
package com.example.alipay.controller.site;
import com.example.alipay.model.site.Station;
import com.example.alipay.model.site.Gate;
import com.example.alipay.service.site.StationManagementService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/site")
public class StationManagementController {
private final StationManagementService stationManagementService;
public StationManagementController(StationManagementService stationManagementService) {
this.stationManagementService = stationManagementService;
}
// 站点管理API
@GetMapping("/stations")
public ResponseEntity<?> getAllStations() {
List<Station> stations = stationManagementService.getAllStations();
return ResponseEntity.ok(stations);
}
@GetMapping("/stations/active")
public ResponseEntity<?> getActiveStations() {
List<Station> stations = stationManagementService.getActiveStations();
return ResponseEntity.ok(stations);
}
@GetMapping("/stations/{stationId}")
public ResponseEntity<?> getStationById(@PathVariable Long stationId) {
return stationManagementService.getStationById(stationId)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping("/stations")
public ResponseEntity<?> createStation(@RequestBody Station station) {
try {
Station createdStation = stationManagementService.createStation(station);
return ResponseEntity.ok(Map.of(
"message", "站点创建成功",
"stationId", createdStation.getId()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
}
}
@PutMapping("/stations/{stationId}")
public ResponseEntity<?> updateStation(@PathVariable Long stationId, @RequestBody Station stationDetails) {
Station updatedStation = stationManagementService.updateStation(stationId, stationDetails);
if (updatedStation != null) {
return ResponseEntity.ok(Map.of(
"message", "站点更新成功",
"stationId", updatedStation.getId()
));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "站点不存在"));
}
}
@PostMapping("/stations/{stationId}/enable")
public ResponseEntity<?> enableStation(@PathVariable Long stationId) {
boolean success = stationManagementService.enableStation(stationId);
if (success) {
return ResponseEntity.ok(Map.of("message", "站点已启用"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "站点不存在"));
}
}
@PostMapping("/stations/{stationId}/disable")
public ResponseEntity<?> disableStation(@PathVariable Long stationId) {
boolean success = stationManagementService.disableStation(stationId);
if (success) {
return ResponseEntity.ok(Map.of("message", "站点已禁用"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "站点不存在"));
}
}
// 闸机管理API
@GetMapping("/stations/{stationId}/gates")
public ResponseEntity<?> getGatesByStation(@PathVariable Long stationId) {
List<Gate> gates = stationManagementService.getGatesByStation(stationId);
return ResponseEntity.ok(gates);
}
@GetMapping("/gates/direction/{direction}")
public ResponseEntity<?> getGatesByDirection(@PathVariable String direction) {
List<Gate> gates = stationManagementService.getGatesByDirection(direction);
return ResponseEntity.ok(gates);
}
@PostMapping("/gates")
public ResponseEntity<?> createGate(@RequestBody Gate gate) {
try {
Gate createdGate = stationManagementService.createGate(gate);
return ResponseEntity.ok(Map.of(
"message", "闸机创建成功",
"gateId", createdGate.getId()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
}
}
@PutMapping("/gates/{gateId}")
public ResponseEntity<?> updateGate(@PathVariable Long gateId, @RequestBody Gate gateDetails) {
Gate updatedGate = stationManagementService.updateGate(gateId, gateDetails);
if (updatedGate != null) {
return ResponseEntity.ok(Map.of(
"message", "闸机更新成功",
"gateId", updatedGate.getId()
));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "闸机不存在"));
}
}
@PostMapping("/gates/{gateId}/enable")
public ResponseEntity<?> enableGate(@PathVariable Long gateId) {
boolean success = stationManagementService.enableGate(gateId);
if (success) {
return ResponseEntity.ok(Map.of("message", "闸机已启用"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "闸机不存在"));
}
}
@PostMapping("/gates/{gateId}/disable")
public ResponseEntity<?> disableGate(@PathVariable Long gateId) {
boolean success = stationManagementService.disableGate(gateId);
if (success) {
return ResponseEntity.ok(Map.of("message", "闸机已禁用"));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "闸机不存在"));
}
}
@DeleteMapping("/gates/{gateId}")
public ResponseEntity<?> deleteGate(@PathVariable Long gateId) {
try {
boolean success = stationManagementService.deleteGate(gateId);
if (success) {
return ResponseEntity.ok(Map.of("message", "闸机已删除", "gateId", gateId));
} else {
return ResponseEntity.badRequest().body(Map.of("error", "闸机不存在"));
}
} catch (IllegalStateException e) {
return ResponseEntity.status(409).body(Map.of("error", e.getMessage()));
}
}
// 查询API
@GetMapping("/stations/city/{city}")
public ResponseEntity<?> getStationsByCity(@PathVariable String city) {
List<Station> stations = stationManagementService.getStationsByCity(city);
return ResponseEntity.ok(stations);
}
@GetMapping("/stations/line/{line}")
public ResponseEntity<?> getStationsByLine(@PathVariable String line) {
List<Station> stations = stationManagementService.getStationsByLine(line);
return ResponseEntity.ok(stations);
}
}

@ -1,38 +0,0 @@
package com.example.alipay.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "assistant_messages")
public class AssistantMessage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username; // 对应 UserAccount.username 和 Payment.fromUser
@Column(length = 1000)
private String userContent; // 用户发送的内容
@Column(length = 1000)
private String botReply; // 助手回复的内容
private LocalDateTime createdAt;
public AssistantMessage() {
this.createdAt = LocalDateTime.now();
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getUserContent() { return userContent; }
public void setUserContent(String userContent) { this.userContent = userContent; }
public String getBotReply() { return botReply; }
public void setBotReply(String botReply) { this.botReply = botReply; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

@ -1,25 +0,0 @@
package com.example.alipay.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
public class CollectionQRCode {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String merchantId;
private String content;
private LocalDateTime expireAt;
private boolean active = true;
public Long getId(){return id;}
public void setId(Long id){this.id = id;}
public String getMerchantId(){return merchantId;}
public void setMerchantId(String m){this.merchantId = m;}
public String getContent(){return content;}
public void setContent(String c){this.content = c;}
public LocalDateTime getExpireAt(){return expireAt;}
public void setExpireAt(LocalDateTime t){this.expireAt = t;}
public boolean isActive(){return active;}
public void setActive(boolean a){this.active = a;}
}

@ -1,38 +0,0 @@
package com.example.alipay.model;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
public class Payment {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String fromUser;
private String toMerchant;
private BigDecimal amount;
private String paymentMethod;
private String status;
private LocalDateTime createdAt;
private String errorCode;
private String errorMessage;
public Long getId(){return id;}
public void setId(Long id){this.id = id;}
public String getFromUser(){return fromUser;}
public void setFromUser(String u){this.fromUser = u;}
public String getToMerchant(){return toMerchant;}
public void setToMerchant(String m){this.toMerchant = m;}
public BigDecimal getAmount(){return amount;}
public void setAmount(BigDecimal a){this.amount = a;}
public String getPaymentMethod(){return paymentMethod;}
public void setPaymentMethod(String pm){this.paymentMethod = pm;}
public String getStatus(){return status;}
public void setStatus(String s){this.status = s;}
public LocalDateTime getCreatedAt(){return createdAt;}
public void setCreatedAt(LocalDateTime t){this.createdAt = t;}
public String getErrorCode(){return errorCode;}
public void setErrorCode(String c){this.errorCode = c;}
public String getErrorMessage(){return errorMessage;}
public void setErrorMessage(String m){this.errorMessage = m;}
}

@ -1,32 +0,0 @@
package com.example.alipay.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
public class TravelPass {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String city;
private String line;
private String boundPaymentMethod;
private boolean enabled = true;
private LocalDateTime updatedAt;
// getters/setters
public Long getId(){return id;}
public void setId(Long id){this.id = id;}
public String getUsername(){return username;}
public void setUsername(String u){this.username = u;}
public String getCity(){return city;}
public void setCity(String c){this.city = c;}
public String getLine(){return line;}
public void setLine(String l){this.line = l;}
public String getBoundPaymentMethod(){return boundPaymentMethod;}
public void setBoundPaymentMethod(String b){this.boundPaymentMethod = b;}
public boolean isEnabled(){return enabled;}
public void setEnabled(boolean e){this.enabled = e;}
public java.time.LocalDateTime getUpdatedAt(){return updatedAt;}
public void setUpdatedAt(java.time.LocalDateTime t){this.updatedAt = t;}
}

@ -1,36 +0,0 @@
package com.example.alipay.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.math.BigDecimal;
@Entity
public class TravelRecord {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String city;
private String line;
private LocalDateTime entryTime;
private LocalDateTime exitTime;
private BigDecimal fare;
private String status; // COMPLETED, IN_PROGRESS, EXCEPTION
// getters/setters
public Long getId(){return id;}
public void setId(Long id){this.id = id;}
public String getUsername(){return username;}
public void setUsername(String u){this.username = u;}
public String getCity(){return city;}
public void setCity(String c){this.city = c;}
public String getLine(){return line;}
public void setLine(String l){this.line = l;}
public LocalDateTime getEntryTime(){return entryTime;}
public void setEntryTime(LocalDateTime t){this.entryTime = t;}
public LocalDateTime getExitTime(){return exitTime;}
public void setExitTime(LocalDateTime t){this.exitTime = t;}
public BigDecimal getFare(){return fare;}
public void setFare(BigDecimal f){this.fare = f;}
public String getStatus(){return status;}
public void setStatus(String s){this.status = s;}
}

@ -1,26 +0,0 @@
package com.example.alipay.model;
import jakarta.persistence.*;
import java.math.BigDecimal;
@Entity
public class UserAccount {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String displayName;
private String defaultPaymentMethod; // simple string like "balance" or card id
private BigDecimal balance = new BigDecimal("1000.00"); // default balance for demo
// getters/setters
public Long getId(){return id;}
public void setId(Long id){this.id = id;}
public String getUsername(){return username;}
public void setUsername(String username){this.username = username;}
public String getDisplayName(){return displayName;}
public void setDisplayName(String displayName){this.displayName = displayName;}
public String getDefaultPaymentMethod(){return defaultPaymentMethod;}
public void setDefaultPaymentMethod(String m){this.defaultPaymentMethod = m;}
public BigDecimal getBalance(){return balance;}
public void setBalance(BigDecimal b){this.balance = b;}
}

@ -1,29 +0,0 @@
package com.example.alipay.model.admin;
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
@Entity
@Table(name = "admin_operation_logs")
@Data
public class AdminOperationLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long adminId; // 操作管理员ID
private String adminUsername; // 操作管理员用户名
private String operationType; // 操作类型
private String targetType; // 目标类型 (USER, POLICY, STATION, etc.)
private Long targetId; // 目标ID
private String operationDetails; // 操作详情
private String ipAddress; // 操作IP
private LocalDateTime operationTime;
private boolean success = true; // 操作是否成功
private String errorMessage; // 错误信息
}

@ -1,38 +0,0 @@
package com.example.alipay.model.admin;
import jakarta.persistence.*;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
@Entity
@Table(name = "admin_users")
@Data
public class AdminUser implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String password;
private String role; // SUPER_ADMIN, SITE_MANAGER
private boolean enabled = true;
// Security UserDetails 实现方法
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(() -> "ROLE_" + role);
}
@Override public boolean isAccountNonExpired() { return true; }
@Override public boolean isAccountNonLocked() { return true; }
@Override public boolean isCredentialsNonExpired() { return true; }
@Override public boolean isEnabled() { return enabled; }
}

@ -1,10 +0,0 @@
package com.example.alipay.model.admin;
import lombok.Data;
@Data
public class CreateAdminRequest {
private String username;
private String password;
private String role;
}

@ -1,34 +0,0 @@
package com.example.alipay.model.admin;
import jakarta.persistence.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
@Entity
@Table(name = "discount_policies")
@Data
public class DiscountPolicy {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String policyName; // 策略名称
private String description; // 策略描述
@Column(precision = 5, scale = 2)
private BigDecimal discountRate; // 折扣率 0.8表示8折
private String applicableUserType; // 适用用户类型
private String applicableLines; // 适用线路
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime startTime;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime endTime;
private boolean enabled = true; // 是否启用
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

@ -1,16 +0,0 @@
package com.example.alipay.model.fintech;
import jakarta.persistence.*;
import lombok.Data;
@Entity
@Data
public class BankCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private String bankName;
private String cardNumber;
private Boolean isDefault;
}

@ -1,27 +0,0 @@
package com.example.alipay.model.fintech;
import jakarta.persistence.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Data
public class Bill {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
@Column(precision = 19, scale = 2)
private BigDecimal amount;
private String type;
private String category;
private LocalDateTime transactionTime;
private String remark;
private String sourceType;
private String sourceRefId;
}

@ -1,49 +0,0 @@
package com.example.alipay.model.fintech;
import jakarta.persistence.*;
import lombok.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
@Entity
@Table(name = "fintech_users")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String password;
private String phone;
@Column(precision = 19, scale = 2)
private BigDecimal balance;
@Enumerated(EnumType.STRING)
private UserStatus status;
private String payPassword;
private Integer payPwdFailedAttempts;
private java.time.LocalDateTime payPwdLockedUntil;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
@Override public boolean isAccountNonExpired() { return true; }
@Override public boolean isAccountNonLocked() { return true; }
@Override public boolean isCredentialsNonExpired() { return true; }
@Override public boolean isEnabled() { return this.status == UserStatus.ACTIVE; }
}

@ -1,18 +0,0 @@
package com.example.alipay.model.fintech;
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
@Entity
@Data
public class UserAudit {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private String realName;
private String idCardNumber;
private String rejectReason;
private LocalDateTime submitTime;
}

@ -1,8 +0,0 @@
package com.example.alipay.model.fintech;
public enum UserStatus {
ACTIVE,
INACTIVE,
PENDING_AUDIT,
REJECTED
}

@ -1,28 +0,0 @@
package com.example.alipay.model.site;
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
@Entity
@Table(name = "gates")
@Data
public class Gate {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long stationId; // 所属站点ID
private String gateCode; // 闸机编码
private String gateName; // 闸机名称
private String direction; // 方向: ENTRY/EXIT
private String location; // 具体位置
private String description; // 闸机描述
private boolean enabled = true; // 是否启用
private String status; // 状态: ONLINE/OFFLINE/MAINTENANCE
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

@ -1,36 +0,0 @@
package com.example.alipay.model.site;
import jakarta.persistence.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "gate_events")
@Data
public class GateEvent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long gateId; // 闸机ID
private String gateCode; // 闸机编码
private Long userId; // 用户ID
private String username; // 用户名
private String eventType; // 事件类型: ENTRY/EXIT/ERROR
private String qrCode; // 扫描的二维码内容
@Column(precision = 8, scale = 2)
private BigDecimal fare; // 费用
private String status; // 状态: SUCCESS/FAILED/PENDING
private String errorCode; // 错误代码
private String errorMessage; // 错误信息
private LocalDateTime eventTime; // 事件时间
private LocalDateTime processedTime; // 处理时间
private String transactionId; // 交易ID
private String remark; // 备注
}

@ -1,26 +0,0 @@
package com.example.alipay.model.site;
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
@Entity
@Table(name = "stations")
@Data
public class Station {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String stationCode; // 站点编码
private String stationName; // 站点名称
private String city; // 所在城市
private String line; // 所属线路
private String location; // 地理位置
private String description; // 站点描述
private boolean enabled = true; // 是否启用
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

@ -1,10 +0,0 @@
package com.example.alipay.repository;
import com.example.alipay.model.AssistantMessage;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface AssistantMessageRepository extends JpaRepository<AssistantMessage, Long> {
// 获取某用户的历史对话,按时间倒序
List<AssistantMessage> findByUsernameOrderByCreatedAtDesc(String username);
}

@ -1,7 +0,0 @@
package com.example.alipay.repository;
import com.example.alipay.model.CollectionQRCode;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CollectionQRCodeRepository extends JpaRepository<CollectionQRCode, Long> {
}

@ -1,13 +0,0 @@
package com.example.alipay.repository;
import com.example.alipay.model.Payment;
import org.springframework.data.jpa.repository.JpaRepository;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public interface PaymentRepository extends JpaRepository<Payment, Long> {
List<Payment> findByFromUserAndCreatedAtBetweenAndStatus(String fromUser, LocalDateTime start, LocalDateTime end, String status);
List<Payment> findByFromUserAndAmountGreaterThanEqualAndStatus(String fromUser, BigDecimal minAmount, String status);
}

@ -1,10 +0,0 @@
package com.example.alipay.repository;
import com.example.alipay.model.TravelPass;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface TravelPassRepository extends JpaRepository<TravelPass, Long> {
List<TravelPass> findByUsername(String username);
}

@ -1,12 +0,0 @@
package com.example.alipay.repository;
import com.example.alipay.model.TravelRecord;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface TravelRecordRepository extends JpaRepository<TravelRecord, Long> {
List<TravelRecord> findByUsernameOrderByEntryTimeDesc(String username);
Optional<TravelRecord> findTopByUsernameAndStatusOrderByEntryTimeDesc(String username, String status);
}

@ -1,19 +0,0 @@
package com.example.alipay.repository;
import com.example.alipay.model.UserAccount;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import jakarta.persistence.LockModeType;
import java.util.Optional;
public interface UserAccountRepository extends JpaRepository<UserAccount, Long> {
Optional<UserAccount> findByUsername(String username);
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select u from UserAccount u where u.username = :username")
Optional<UserAccount> findByUsernameForUpdate(@Param("username") String username);
}

@ -1,10 +0,0 @@
package com.example.alipay.repository.admin;
import com.example.alipay.model.admin.AdminOperationLog;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface AdminOperationLogRepository extends JpaRepository<AdminOperationLog, Long> {
List<AdminOperationLog> findByAdminIdOrderByOperationTimeDesc(Long adminId);
List<AdminOperationLog> findByOperationTypeOrderByOperationTimeDesc(String operationType);
}

@ -1,10 +0,0 @@
package com.example.alipay.repository.admin;
import com.example.alipay.model.admin.AdminUser;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface AdminUserRepository extends JpaRepository<AdminUser, Long> {
Optional<AdminUser> findByUsername(String username);
boolean existsByUsername(String username);
}

@ -1,10 +0,0 @@
package com.example.alipay.repository.admin;
import com.example.alipay.model.admin.DiscountPolicy;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface DiscountPolicyRepository extends JpaRepository<DiscountPolicy, Long> {
List<DiscountPolicy> findByEnabledTrue();
List<DiscountPolicy> findByApplicableLinesContaining(String line);
}

@ -1,10 +0,0 @@
package com.example.alipay.repository.fintech;
import com.example.alipay.model.fintech.BankCard;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface BankCardRepository extends JpaRepository<BankCard, Long> {
List<BankCard> findByUserId(Long userId);
List<BankCard> findByUserIdAndIsDefaultTrue(Long userId);
}

@ -1,13 +0,0 @@
package com.example.alipay.repository.fintech;
import com.example.alipay.model.fintech.Bill;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDateTime;
import java.util.List;
public interface BillRepository extends JpaRepository<Bill, Long> {
List<Bill> findByUserIdOrderByTransactionTimeDesc(Long userId);
List<Bill> findByUserIdAndTypeOrderByTransactionTimeDesc(Long userId, String type);
boolean existsByUserIdAndSourceTypeAndSourceRefId(Long userId, String sourceType, String sourceRefId);
List<Bill> findByUserIdAndTypeAndTransactionTimeBetween(Long userId, String type, LocalDateTime start, LocalDateTime end);
}

@ -1,9 +0,0 @@
package com.example.alipay.repository.fintech;
import com.example.alipay.model.fintech.UserAudit;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserAuditRepository extends JpaRepository<UserAudit, Long> {
Optional<UserAudit> findByUserId(Long userId);
}

@ -1,24 +0,0 @@
package com.example.alipay.repository.fintech;
import com.example.alipay.model.fintech.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import jakarta.persistence.LockModeType;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
boolean existsByUsername(String username);
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select u from User u where u.username = :username")
Optional<User> findByUsernameForUpdate(@Param("username") String username);
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select u from User u where u.id = :id")
Optional<User> findByIdForUpdate(@Param("id") Long id);
}

@ -1,17 +0,0 @@
package com.example.alipay.repository.site;
import com.example.alipay.model.site.GateEvent;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface GateEventRepository extends JpaRepository<GateEvent, Long> {
List<GateEvent> findByGateIdOrderByEventTimeDesc(Long gateId);
List<GateEvent> findByUserIdOrderByEventTimeDesc(Long userId);
List<GateEvent> findByEventTypeOrderByEventTimeDesc(String eventType);
List<GateEvent> findByStatusOrderByEventTimeDesc(String status);
Optional<GateEvent> findByTransactionId(String transactionId);
Optional<GateEvent> findTopByUserIdAndEventTypeAndStatusOrderByEventTimeDesc(Long userId, String eventType, String status);
long countByGateId(Long gateId);
}

@ -1,19 +0,0 @@
package com.example.alipay.repository.site;
import com.example.alipay.model.site.Gate;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface GateRepository extends JpaRepository<Gate, Long> {
List<Gate> findByGateCode(String gateCode);
Optional<Gate> findTopByGateCodeOrderByUpdatedAtDesc(String gateCode);
Optional<Gate> findTopByGateCodeAndEnabledTrueOrderByUpdatedAtDesc(String gateCode);
Optional<Gate> findTopByStationIdAndGateCodeOrderByUpdatedAtDesc(Long stationId, String gateCode);
Optional<Gate> findTopByStationIdAndGateCodeAndEnabledTrueOrderByUpdatedAtDesc(Long stationId, String gateCode);
List<Gate> findByGateCodeAndDirection(String gateCode, String direction);
List<Gate> findByStationId(Long stationId);
List<Gate> findByDirection(String direction);
List<Gate> findByEnabledTrue();
List<Gate> findByStationIdAndDirection(Long stationId, String direction);
}

@ -1,13 +0,0 @@
package com.example.alipay.repository.site;
import com.example.alipay.model.site.Station;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface StationRepository extends JpaRepository<Station, Long> {
Optional<Station> findByStationCode(String stationCode);
List<Station> findByCity(String city);
List<Station> findByLine(String line);
List<Station> findByEnabledTrue();
}

@ -1,36 +0,0 @@
package com.example.alipay.security.fintech;
import com.example.alipay.model.admin.AdminUser;
import com.example.alipay.model.fintech.User;
import com.example.alipay.repository.admin.AdminUserRepository;
import com.example.alipay.repository.fintech.UserRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
private final AdminUserRepository adminUserRepository;
public CustomUserDetailsService(UserRepository userRepository, AdminUserRepository adminUserRepository) {
this.userRepository = userRepository;
this.adminUserRepository = adminUserRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> userOpt = userRepository.findByUsername(username);
if (userOpt.isPresent()) {
return userOpt.get();
}
Optional<AdminUser> adminOpt = adminUserRepository.findByUsername(username);
if (adminOpt.isPresent()) {
return adminOpt.get();
}
throw new UsernameNotFoundException("用户不存在: " + username);
}
}

@ -1,49 +0,0 @@
package com.example.alipay.security.fintech;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final CustomUserDetailsService userDetailsService;
private final JwtUtil jwtUtil;
public JwtAuthenticationFilter(CustomUserDetailsService userDetailsService, JwtUtil jwtUtil) {
this.userDetailsService = userDetailsService;
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String username = null;
String jwt = null;
try {
final String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, username)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
} catch (Exception ignored) {
}
chain.doFilter(request, response);
}
}

@ -1,46 +0,0 @@
package com.example.alipay.security.fintech;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
@Component
public class JwtUtil {
private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private final long expiration = 86400000; // 24小时
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(key)
.compact();
}
public String extractUsername(String token) {
return extractClaims(token).getSubject();
}
public boolean validateToken(String token, String username) {
final String extractedUsername = extractUsername(token);
return (extractedUsername.equals(username) && !isTokenExpired(token));
}
private Claims extractClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
}
private boolean isTokenExpired(String token) {
return extractClaims(token).getExpiration().before(new Date());
}
}

@ -1,43 +0,0 @@
package com.example.alipay.service;
import com.example.alipay.model.CollectionQRCode;
import com.example.alipay.repository.CollectionQRCodeRepository;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.UUID;
@Service
public class CollectionService {
private final CollectionQRCodeRepository repo;
private final QrService qrService;
public CollectionService(CollectionQRCodeRepository repo, QrService qrService){
this.repo = repo;
this.qrService = qrService;
}
public CollectionQRCode generateInitialQRCode(String merchantId, int validSeconds){
CollectionQRCode q = new CollectionQRCode();
q.setMerchantId(merchantId);
String content = "collect://" + merchantId + "/" + UUID.randomUUID();
q.setContent(content);
q.setExpireAt(LocalDateTime.now().plusSeconds(validSeconds));
q.setActive(true);
repo.save(q);
return q;
}
public CollectionQRCode refreshQRCode(Long id, int validSeconds){
CollectionQRCode q = repo.findById(id).orElseThrow();
q.setContent("collect://" + q.getMerchantId() + "/" + UUID.randomUUID());
q.setExpireAt(LocalDateTime.now().plusSeconds(validSeconds));
q.setActive(true);
repo.save(q);
return q;
}
public boolean isValid(CollectionQRCode q){
return q != null && q.isActive() && q.getExpireAt()!=null && q.getExpireAt().isAfter(LocalDateTime.now());
}
}

@ -1,213 +0,0 @@
package com.example.alipay.service;
import com.example.alipay.model.Payment;
import com.example.alipay.model.UserAccount;
import com.example.alipay.model.fintech.User;
import com.example.alipay.model.fintech.UserStatus;
import com.example.alipay.model.fintech.BankCard;
import com.example.alipay.model.fintech.Bill;
import com.example.alipay.repository.PaymentRepository;
import com.example.alipay.repository.UserAccountRepository;
import com.example.alipay.repository.fintech.UserRepository;
import com.example.alipay.repository.fintech.BankCardRepository;
import com.example.alipay.repository.fintech.BillRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Optional;
@Service
public class PaymentService {
private final PaymentRepository paymentRepository;
private final UserRepository userRepository;
private final UserAccountRepository userAccountRepository;
private final BankCardRepository bankCardRepository;
private final PasswordEncoder passwordEncoder;
private final BillRepository billRepository;
public PaymentService(
PaymentRepository paymentRepository,
UserRepository userRepository,
UserAccountRepository userAccountRepository,
BankCardRepository bankCardRepository,
PasswordEncoder passwordEncoder,
BillRepository billRepository
){
this.paymentRepository = paymentRepository;
this.userRepository = userRepository;
this.userAccountRepository = userAccountRepository;
this.bankCardRepository = bankCardRepository;
this.passwordEncoder = passwordEncoder;
this.billRepository = billRepository;
}
public String preCheck(String username, String payPassword) {
Optional<User> opt = userRepository.findByUsername(username);
if (opt.isEmpty()) return "USER_NOT_FOUND";
User u = opt.get();
if (u.getPayPwdLockedUntil() != null && u.getPayPwdLockedUntil().isAfter(java.time.LocalDateTime.now())) return "LOCKED";
if (payPassword == null || u.getPayPassword() == null || !passwordEncoder.matches(payPassword, u.getPayPassword())) {
int attempts = u.getPayPwdFailedAttempts() == null ? 0 : u.getPayPwdFailedAttempts();
attempts++;
u.setPayPwdFailedAttempts(attempts);
if (attempts >= 5) {
u.setPayPwdLockedUntil(java.time.LocalDateTime.now().plusMinutes(30));
u.setPayPwdFailedAttempts(0);
}
userRepository.save(u);
return "INVALID_PASSWORD";
}
u.setPayPwdFailedAttempts(0);
u.setPayPwdLockedUntil(null);
userRepository.save(u);
if (u.getStatus() == UserStatus.PENDING_AUDIT) return "PENDING_AUDIT";
if (u.getStatus() == UserStatus.REJECTED) return "REJECTED";
if (u.getStatus() == UserStatus.INACTIVE) return "INACTIVE";
return null;
}
@Transactional
public Payment createAndExecutePayment(String fromUser, String toMerchant, BigDecimal amount, String paymentMethod, String paySource, Long payCardId, String receiveSource, Long receiveCardId){
String normalizedFromUser = normalizeUserKey(fromUser);
String normalizedToMerchant = normalizeUserKey(toMerchant);
Payment p = new Payment();
p.setFromUser(fromUser);
p.setToMerchant(toMerchant);
p.setAmount(amount);
p.setPaymentMethod(paymentMethod);
p.setStatus("INIT");
p.setCreatedAt(LocalDateTime.now());
paymentRepository.save(p);
boolean receiveOnly = (paySource == null || paySource.isBlank());
if (receiveOnly) {
Optional<User> toFintechOpt = userRepository.findByUsernameForUpdate(normalizedToMerchant);
Optional<UserAccount> toAccountOpt = toFintechOpt.isPresent()
? Optional.empty()
: userAccountRepository.findByUsernameForUpdate(normalizedToMerchant);
if (toFintechOpt.isPresent()) {
User to = toFintechOpt.get();
to.setBalance(to.getBalance() == null ? amount : to.getBalance().add(amount));
userRepository.save(to);
p.setStatus("SUCCESS");
createBillIfNotExists(to.getId(), amount, "INCOME", "收款", "收款入账", "PAYMENT", "PAY:" + p.getId());
} else if (toAccountOpt.isPresent()) {
UserAccount to = toAccountOpt.get();
BigDecimal toBalance = to.getBalance() == null ? BigDecimal.ZERO : to.getBalance();
to.setBalance(toBalance.add(amount));
userAccountRepository.save(to);
p.setStatus("SUCCESS");
} else {
p.setStatus("FAILED");
p.setErrorCode("RECEIVER_NOT_FOUND");
p.setErrorMessage("receiver not found");
}
paymentRepository.save(p);
return p;
}
Optional<User> fromOpt = userRepository.findByUsernameForUpdate(normalizedFromUser);
if (fromOpt.isEmpty()) {
p.setStatus("FAILED");
paymentRepository.save(p);
return p;
}
User from = fromOpt.get();
boolean deducted = false;
if ("balance".equalsIgnoreCase(paySource)) {
if (from.getBalance() != null && from.getBalance().compareTo(amount) >= 0) {
from.setBalance(from.getBalance().subtract(amount));
userRepository.save(from);
deducted = true;
} else {
p.setStatus("FAILED");
p.setErrorCode("INSUFFICIENT_FUNDS");
p.setErrorMessage("insufficient balance");
}
} else if ("card".equalsIgnoreCase(paySource)) {
if (payCardId != null) {
Optional<BankCard> cardOpt = bankCardRepository.findById(payCardId);
if (cardOpt.isPresent() && cardOpt.get().getUserId().equals(from.getId())) {
deducted = true;
} else {
p.setStatus("FAILED");
p.setErrorCode(cardOpt.isEmpty() ? "CARD_NOT_FOUND" : "CARD_OWNERSHIP_ERROR");
p.setErrorMessage("invalid card");
}
} else {
p.setStatus("FAILED");
p.setErrorCode("CARD_NOT_FOUND");
p.setErrorMessage("card required");
}
} else {
p.setStatus("FAILED");
p.setErrorCode("INVALID_PAY_SOURCE");
p.setErrorMessage("invalid pay source");
}
if (!"FAILED".equals(p.getStatus())) {
Optional<User> toFintechOpt = userRepository.findByUsernameForUpdate(normalizedToMerchant);
Optional<UserAccount> toAccountOpt = toFintechOpt.isPresent()
? Optional.empty()
: userAccountRepository.findByUsernameForUpdate(normalizedToMerchant);
if (deducted) {
createBillIfNotExists(from.getId(), amount, "EXPENDITURE", "支付", "支付给:" + normalizedToMerchant, "PAYMENT", "PAY:" + p.getId());
}
if (toFintechOpt.isPresent()) {
User to = toFintechOpt.get();
to.setBalance(to.getBalance() == null ? amount : to.getBalance().add(amount));
userRepository.save(to);
if (deducted) {
createBillIfNotExists(to.getId(), amount, "INCOME", "收款", "来自:" + normalizedFromUser, "PAYMENT", "PAY:" + p.getId());
}
} else if (toAccountOpt.isPresent()) {
UserAccount to = toAccountOpt.get();
BigDecimal toBalance = to.getBalance() == null ? BigDecimal.ZERO : to.getBalance();
to.setBalance(toBalance.add(amount));
userAccountRepository.save(to);
}
p.setStatus(deducted ? "SUCCESS" : "FAILED");
}
paymentRepository.save(p);
return p;
}
private String normalizeUserKey(String raw) {
if (raw == null) return null;
String v = raw.trim();
if (v.isEmpty()) return v;
if (v.startsWith("collect://")) {
v = v.substring("collect://".length());
}
int slash = v.indexOf('/');
if (slash > 0) {
return v.substring(0, slash);
}
return v;
}
private void createBillIfNotExists(Long userId, BigDecimal amount, String type, String category, String remark, String sourceType, String sourceRefId) {
if (userId == null) return;
if (billRepository.existsByUserIdAndSourceTypeAndSourceRefId(userId, sourceType, sourceRefId)) return;
Bill b = new Bill();
b.setUserId(userId);
b.setAmount(amount);
b.setType(type);
b.setCategory(category);
b.setTransactionTime(java.time.LocalDateTime.now());
b.setRemark(remark);
b.setSourceType(sourceType);
b.setSourceRefId(sourceRefId);
billRepository.save(b);
}
}

@ -1,92 +0,0 @@
package com.example.alipay.service;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
@Service
public class QrService {
public String decodeQrFromImage(MultipartFile file) {
try {
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
if (bufferedImage == null) {
throw new RuntimeException("无法读取图片文件");
}
LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result = new MultiFormatReader().decode(bitmap);
return result.getText();
} catch (NotFoundException e) {
throw new RuntimeException("未检测到二维码");
} catch (IOException e) {
throw new RuntimeException("图片读取失败");
}
}
public String decodeBufferedImage(BufferedImage image) {
try {
LuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result = new MultiFormatReader().decode(bitmap);
return result.getText();
} catch (NotFoundException e) {
throw new RuntimeException("二维码识别失败");
}
}
public String generateQrBase64(String content, int width, int height) {
try {
BitMatrix bitMatrix = new MultiFormatWriter()
.encode(content, BarcodeFormat.QR_CODE, width, height);
ByteArrayOutputStream out = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", out);
String base64 = Base64.getEncoder().encodeToString(out.toByteArray());
return "data:image/png;base64," + base64;
} catch (WriterException | IOException e) {
throw new RuntimeException("二维码生成失败", e);
}
}
public String parseQrFromBytes(byte[] bytes) {
try {
BufferedImage image = ImageIO.read(new java.io.ByteArrayInputStream(bytes));
if (image == null) {
throw new RuntimeException("二维码图片格式不正确或无法读取");
}
LuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result = new MultiFormatReader().decode(bitmap);
return result.getText();
} catch (NotFoundException e) {
throw new RuntimeException("未识别到二维码内容");
} catch (IOException e) {
throw new RuntimeException("二维码字节流解析失败");
}
}
}

@ -1,186 +0,0 @@
package com.example.alipay.service;
import com.example.alipay.model.AssistantMessage;
import com.example.alipay.model.Payment;
import com.example.alipay.model.fintech.Bill;
import com.example.alipay.model.fintech.User;
import com.example.alipay.repository.AssistantMessageRepository;
import com.example.alipay.repository.PaymentRepository;
import com.example.alipay.repository.fintech.BillRepository;
import com.example.alipay.repository.fintech.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
@Service
public class SmartAssistantService {
@Autowired
private AssistantMessageRepository messageRepository;
@Autowired
private PaymentRepository paymentRepository;
@Autowired
private BillRepository billRepository;
@Autowired
private UserRepository userRepository;
// 读取配置文件中的 AI 设置
@Value("${ai.api.url}")
private String apiUrl;
@Value("${ai.api.key}")
private String apiKey;
@Value("${ai.api.model}")
private String apiModel;
private final RestTemplate restTemplate = new RestTemplate();
public String getWelcomeMessage() {
return "您好!我是您的 AI 财务助手。我已连接到您的账单数据库,您可以问我:‘我总共花了多少钱?’、‘有没有什么异常支出?’ 或者 ‘帮我分析一下消费习惯’。";
}
public AssistantMessage processChat(String username, String userContent) {
Map<String, Object> monthlyStats = analyzeMonthly(username);
List<Bill> largePayments = getLargePayments(username);
StringBuilder systemPrompt = new StringBuilder();
systemPrompt.append("你是一个专业的财务助手。");
systemPrompt.append("当前用户名为: ").append(username).append("。");
systemPrompt.append("以下是该用户的账单汇总数据,请根据这些数据回答用户的问题:\n");
systemPrompt.append("1. 总支出: ").append(monthlyStats.get("totalExpenditure")).append("元, 总收入: ")
.append(monthlyStats.get("totalIncome")).append("元\n");
systemPrompt.append("2. 本月交易笔数: ").append(monthlyStats.get("count")).append("笔\n");
systemPrompt.append("3. 财务健康建议: ").append(monthlyStats.get("advice")).append("\n");
if (!largePayments.isEmpty()) {
systemPrompt.append("4. 近期大额支出记录(超过500元): ");
for (Bill p : largePayments) {
java.math.BigDecimal a = p.getAmount() == null ? java.math.BigDecimal.ZERO : p.getAmount().abs();
java.time.LocalDate d = p.getTransactionTime() != null ? p.getTransactionTime().toLocalDate() : java.time.LocalDate.now();
systemPrompt.append("[").append(d)
.append(" 消费 ").append(a).append("元], ");
}
} else {
systemPrompt.append("4. 近期无大额异常支出。");
}
systemPrompt.append("\n请用自然、亲切的语气简短回答用户。如果用户问无关问题礼貌回绝。");
String botReply = callAiApi(systemPrompt.toString(), userContent, username);
AssistantMessage message = new AssistantMessage();
message.setUsername(username);
message.setUserContent(userContent);
message.setBotReply(botReply);
return messageRepository.save(message);
}
private String callAiApi(String systemContext, String userMessage, String username) {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + apiKey);
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", apiModel);
requestBody.put("temperature", 0.7);
List<Map<String, String>> messages = new ArrayList<>();
messages.add(Map.of("role", "system", "content", systemContext));
messages.add(Map.of("role", "user", "content", userMessage));
requestBody.put("messages", messages);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<Map> response = restTemplate.postForEntity(apiUrl, entity, Map.class);
if (response.getBody() != null) {
List<Map<String, Object>> choices = (List<Map<String, Object>>) response.getBody().get("choices");
if (choices != null && !choices.isEmpty()) {
Map<String, Object> messageObj = (Map<String, Object>) choices.get(0).get("message");
return (String) messageObj.get("content");
}
}
return "AI 响应异常,请稍后再试。";
} catch (Exception e) {
e.printStackTrace();
return "抱歉AI 服务暂时不可用 (" + e.getMessage() + ")。但根据数据,您本月消费了 "
+ analyzeMonthly(username).get("total") + " 元。";
}
}
public Map<String, Object> analyzeMonthly(String username) {
Optional<User> userOpt = userRepository.findByUsername(username);
if (userOpt.isEmpty()) {
Map<String, Object> empty = new HashMap<>();
empty.put("total", BigDecimal.ZERO);
empty.put("totalExpenditure", BigDecimal.ZERO);
empty.put("totalIncome", BigDecimal.ZERO);
empty.put("advice", "消费保持在合理范围内。");
empty.put("count", 0);
return empty;
}
Long userId = userOpt.get().getId();
LocalDateTime now = LocalDateTime.now();
LocalDateTime start = now.with(TemporalAdjusters.firstDayOfMonth()).withHour(0).withMinute(0).withSecond(0);
LocalDateTime end = now.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59);
List<Bill> monthlyExp = billRepository.findByUserIdAndTypeAndTransactionTimeBetween(userId, "EXPENDITURE", start, end);
List<Bill> monthlyInc = billRepository.findByUserIdAndTypeAndTransactionTimeBetween(userId, "INCOME", start, end);
List<Bill> allExp = billRepository.findByUserIdAndTypeOrderByTransactionTimeDesc(userId, "EXPENDITURE");
List<Bill> allInc = billRepository.findByUserIdAndTypeOrderByTransactionTimeDesc(userId, "INCOME");
BigDecimal monthlyTotalExp = monthlyExp.stream()
.map(b -> b.getAmount() == null ? BigDecimal.ZERO : b.getAmount().abs())
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal allTotalExp = allExp.stream()
.map(b -> b.getAmount() == null ? BigDecimal.ZERO : b.getAmount().abs())
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal allTotalInc = allInc.stream()
.map(b -> b.getAmount() == null ? BigDecimal.ZERO : b.getAmount().abs())
.reduce(BigDecimal.ZERO, BigDecimal::add);
String advice;
if (monthlyTotalExp.compareTo(new BigDecimal("10000")) > 0) advice = "警告:本月支出已大幅超出平均水平!";
else if (monthlyTotalExp.compareTo(new BigDecimal("5000")) > 0) advice = "本月支出较高,建议关注娱乐类消费。";
else advice = "消费保持在合理范围内。";
Map<String, Object> result = new HashMap<>();
result.put("total", monthlyTotalExp);
result.put("totalExpenditure", allTotalExp);
result.put("totalIncome", allTotalInc);
result.put("advice", advice);
result.put("count", monthlyExp.size() + monthlyInc.size());
return result;
}
public List<Bill> getLargePayments(String username) {
Optional<User> userOpt = userRepository.findByUsername(username);
if (userOpt.isEmpty()) return java.util.Collections.emptyList();
Long userId = userOpt.get().getId();
BigDecimal threshold = new BigDecimal("500.00");
List<Bill> bills = billRepository.findByUserIdAndTypeOrderByTransactionTimeDesc(userId, "EXPENDITURE");
return bills.stream().filter(b -> {
BigDecimal a = b.getAmount() == null ? BigDecimal.ZERO : b.getAmount().abs();
return a.compareTo(threshold) >= 0;
}).toList();
}
}

@ -1,151 +0,0 @@
package com.example.alipay.service;
import com.example.alipay.model.TravelPass;
import com.example.alipay.model.TravelRecord;
import com.example.alipay.repository.TravelPassRepository;
import com.example.alipay.repository.TravelRecordRepository;
import com.example.alipay.model.fintech.User;
import com.example.alipay.repository.fintech.UserRepository;
import com.example.alipay.model.fintech.BankCard;
import com.example.alipay.model.fintech.Bill;
import com.example.alipay.repository.fintech.BillRepository;
import com.example.alipay.repository.fintech.BankCardRepository;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
public class TravelService {
private final TravelPassRepository passRepo;
private final TravelRecordRepository recordRepo;
private final UserRepository fintechUserRepo;
private final BankCardRepository bankCardRepo;
private final BillRepository billRepo;
public TravelService(TravelPassRepository passRepo, TravelRecordRepository recordRepo, UserRepository fintechUserRepo, BankCardRepository bankCardRepo, BillRepository billRepo){
this.passRepo = passRepo;
this.recordRepo = recordRepo;
this.fintechUserRepo = fintechUserRepo;
this.bankCardRepo = bankCardRepo;
this.billRepo = billRepo;
}
public TravelPass openTravelPass(String username, String city, String line, String defaultPayment){
List<TravelPass> existing = passRepo.findByUsername(username);
TravelPass p = null;
if (existing != null && !existing.isEmpty()) {
Optional<TravelPass> match = existing.stream().filter(tp -> city.equals(tp.getCity()) && line.equals(tp.getLine())).findFirst();
p = match.orElse(existing.get(0));
p.setCity(city);
p.setLine(line);
p.setBoundPaymentMethod(defaultPayment);
p.setEnabled(true);
p.setUpdatedAt(LocalDateTime.now());
passRepo.save(p);
} else {
p = new TravelPass();
p.setUsername(username);
p.setCity(city);
p.setLine(line);
p.setBoundPaymentMethod(defaultPayment);
p.setEnabled(true);
p.setUpdatedAt(LocalDateTime.now());
passRepo.save(p);
}
return p;
}
public TravelRecord entry(String username, String city, String line){
TravelRecord r = new TravelRecord();
r.setUsername(username);
r.setCity(city);
r.setLine(line);
r.setEntryTime(LocalDateTime.now());
r.setStatus("IN_PROGRESS");
recordRepo.save(r);
if ("COMPLETED".equals(r.getStatus())) {
Optional<User> uOpt = fintechUserRepo.findByUsername(r.getUsername());
if (uOpt.isPresent()) {
Long userId = uOpt.get().getId();
String ref = "TRV:" + r.getId();
if (!billRepo.existsByUserIdAndSourceTypeAndSourceRefId(userId, "TRAVEL", ref)) {
Bill b = new Bill();
b.setUserId(userId);
b.setAmount(r.getFare());
b.setType("EXPENDITURE");
b.setCategory("交通");
b.setTransactionTime(LocalDateTime.now());
b.setRemark("地铁出站扣费");
b.setSourceType("TRAVEL");
b.setSourceRefId(ref);
billRepo.save(b);
}
}
}
return r;
}
public TravelRecord exit(Long recordId){
TravelRecord r = recordRepo.findById(recordId).orElseThrow();
r.setExitTime(LocalDateTime.now());
r.setFare(new BigDecimal("2.50"));
r.setStatus("COMPLETED");
List<TravelPass> passes = passRepo.findByUsername(r.getUsername());
String payMethod = (passes != null && !passes.isEmpty()) ? passes.get(0).getBoundPaymentMethod() : "balance";
Optional<User> userOpt = fintechUserRepo.findByUsername(r.getUsername());
if (userOpt.isPresent()) {
User u = userOpt.get();
if ("balance".equalsIgnoreCase(payMethod)) {
if (u.getBalance() != null && u.getBalance().compareTo(r.getFare()) >= 0) {
u.setBalance(u.getBalance().subtract(r.getFare()));
fintechUserRepo.save(u);
} else {
r.setStatus("EXCEPTION");
}
} else if ("card".equalsIgnoreCase(payMethod)) {
List<BankCard> defaults = bankCardRepo.findByUserIdAndIsDefaultTrue(u.getId());
if (defaults != null && !defaults.isEmpty()) {
// 模拟卡扣款成功(外部通道),不改动余额
} else {
r.setStatus("EXCEPTION");
}
} else {
r.setStatus("EXCEPTION");
}
} else {
r.setStatus("EXCEPTION");
}
recordRepo.save(r);
if ("COMPLETED".equals(r.getStatus())) {
Optional<User> uOpt2 = fintechUserRepo.findByUsername(r.getUsername());
if (uOpt2.isPresent()) {
Long userId = uOpt2.get().getId();
String ref = "TRV:" + r.getId();
if (!billRepo.existsByUserIdAndSourceTypeAndSourceRefId(userId, "TRAVEL", ref)) {
Bill b = new Bill();
b.setUserId(userId);
b.setAmount(r.getFare());
b.setType("EXPENDITURE");
b.setCategory("交通");
b.setTransactionTime(LocalDateTime.now());
b.setRemark("地铁出站扣费");
b.setSourceType("TRAVEL");
b.setSourceRefId(ref);
billRepo.save(b);
}
}
}
return r;
}
public List<TravelRecord> listRecords(String username){
return recordRepo.findByUsernameOrderByEntryTimeDesc(username);
}
}

@ -1,65 +0,0 @@
package com.example.alipay.service.admin;
import com.example.alipay.model.admin.AdminUser;
import com.example.alipay.repository.admin.AdminUserRepository;
import com.example.alipay.service.admin.AdminOperationLogger;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class AdminAuthService {
private final AdminUserRepository adminUserRepository;
private final PasswordEncoder passwordEncoder;
private final AdminOperationLogger adminOperationLogger;
public AdminAuthService(AdminUserRepository adminUserRepository, PasswordEncoder passwordEncoder, AdminOperationLogger adminOperationLogger) {
this.adminUserRepository = adminUserRepository;
this.passwordEncoder = passwordEncoder;
this.adminOperationLogger = adminOperationLogger;
}
public AdminUser createAdmin(String username, String password, String role) {
if (adminUserRepository.existsByUsername(username)) {
throw new RuntimeException("管理员用户名已存在");
}
AdminUser admin = new AdminUser();
admin.setUsername(username);
admin.setPassword(passwordEncoder.encode(password));
admin.setRole(role);
admin.setEnabled(true);
AdminUser saved = adminUserRepository.save(admin);
adminOperationLogger.log("ADMIN_CREATE", "ADMIN", saved.getId(), "create admin", true, null);
return saved;
}
public Optional<AdminUser> authenticate(String username, String password) {
Optional<AdminUser> adminOpt = adminUserRepository.findByUsername(username);
if (adminOpt.isPresent() && passwordEncoder.matches(password, adminOpt.get().getPassword())) {
return adminOpt;
}
return Optional.empty();
}
public boolean hasPermission(AdminUser admin, String requiredRole) {
return admin.getRole().equals(requiredRole) || admin.getRole().equals("SUPER_ADMIN");
}
public List<AdminUser> getAllAdmins() {
return adminUserRepository.findAll();
}
public boolean deleteAdmin(Long adminId) {
if (adminUserRepository.existsById(adminId)) {
adminUserRepository.deleteById(adminId);
adminOperationLogger.log("ADMIN_DELETE", "ADMIN", adminId, "delete admin", true, null);
return true;
}
adminOperationLogger.log("ADMIN_DELETE", "ADMIN", adminId, "delete admin", false, "admin not found");
return false;
}
}

@ -1,28 +0,0 @@
package com.example.alipay.service.admin;
import com.example.alipay.model.admin.AdminOperationLog;
import com.example.alipay.repository.admin.AdminOperationLogRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AdminOperationLogService {
private final AdminOperationLogRepository logRepository;
public AdminOperationLogService(AdminOperationLogRepository logRepository) {
this.logRepository = logRepository;
}
public List<AdminOperationLog> getAllLogs() {
return logRepository.findAll();
}
public List<AdminOperationLog> getLogsByAdmin(Long adminId) {
return logRepository.findByAdminIdOrderByOperationTimeDesc(adminId);
}
public List<AdminOperationLog> getLogsByType(String operationType) {
return logRepository.findByOperationTypeOrderByOperationTimeDesc(operationType);
}
}

@ -1,68 +0,0 @@
package com.example.alipay.service.admin;
import com.example.alipay.model.admin.AdminOperationLog;
import com.example.alipay.model.admin.AdminUser;
import com.example.alipay.repository.admin.AdminOperationLogRepository;
import com.example.alipay.repository.admin.AdminUserRepository;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Optional;
@Service
public class AdminOperationLogger {
private final AdminOperationLogRepository logRepository;
private final AdminUserRepository adminUserRepository;
public AdminOperationLogger(AdminOperationLogRepository logRepository, AdminUserRepository adminUserRepository) {
this.logRepository = logRepository;
this.adminUserRepository = adminUserRepository;
}
public void log(String operationType, String targetType, Long targetId, String operationDetails, boolean success, String errorMessage) {
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth != null ? auth.getName() : null;
Long adminId = null;
if (auth != null && auth.getPrincipal() instanceof AdminUser au) {
adminId = au.getId();
username = au.getUsername();
} else if (username != null) {
Optional<AdminUser> adminOpt = adminUserRepository.findByUsername(username);
if (adminOpt.isPresent()) {
adminId = adminOpt.get().getId();
}
}
String ip = null;
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs != null) {
HttpServletRequest request = attrs.getRequest();
ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isBlank()) {
ip = request.getRemoteAddr();
}
}
AdminOperationLog log = new AdminOperationLog();
log.setAdminId(adminId);
log.setAdminUsername(username);
log.setOperationType(operationType);
log.setTargetType(targetType);
log.setTargetId(targetId);
log.setOperationDetails(operationDetails);
log.setIpAddress(ip);
log.setOperationTime(LocalDateTime.now());
log.setSuccess(success);
log.setErrorMessage(errorMessage);
logRepository.save(log);
} catch (Exception ignored) {
}
}
}

@ -1,113 +0,0 @@
package com.example.alipay.service.admin;
import com.example.alipay.model.admin.DiscountPolicy;
import com.example.alipay.repository.admin.DiscountPolicyRepository;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
public class DiscountPolicyService {
private final DiscountPolicyRepository discountPolicyRepository;
private final AdminOperationLogger adminOperationLogger;
public DiscountPolicyService(DiscountPolicyRepository discountPolicyRepository, AdminOperationLogger adminOperationLogger) {
this.discountPolicyRepository = discountPolicyRepository;
this.adminOperationLogger = adminOperationLogger;
}
public List<DiscountPolicy> getAllPolicies() {
return discountPolicyRepository.findAll();
}
public List<DiscountPolicy> getActivePolicies() {
return discountPolicyRepository.findByEnabledTrue();
}
public Optional<DiscountPolicy> getPolicyById(Long policyId) {
return discountPolicyRepository.findById(policyId);
}
public DiscountPolicy createPolicy(DiscountPolicy policy) {
if (policy.getApplicableUserType() != null && policy.getApplicableUserType().isBlank()) {
policy.setApplicableUserType(null);
}
if (policy.getApplicableLines() != null) {
policy.setApplicableLines(policy.getApplicableLines().trim());
}
policy.setCreatedAt(LocalDateTime.now());
policy.setUpdatedAt(LocalDateTime.now());
DiscountPolicy saved = discountPolicyRepository.save(policy);
adminOperationLogger.log("POLICY_CREATE", "POLICY", saved.getId(), "create policy", true, null);
return saved;
}
public DiscountPolicy updatePolicy(Long policyId, DiscountPolicy policyDetails) {
Optional<DiscountPolicy> policyOpt = discountPolicyRepository.findById(policyId);
if (policyOpt.isPresent()) {
DiscountPolicy policy = policyOpt.get();
policy.setPolicyName(policyDetails.getPolicyName());
policy.setDescription(policyDetails.getDescription());
policy.setDiscountRate(policyDetails.getDiscountRate());
policy.setApplicableUserType(
policyDetails.getApplicableUserType() != null && policyDetails.getApplicableUserType().isBlank()
? null
: policyDetails.getApplicableUserType()
);
policy.setApplicableLines(policyDetails.getApplicableLines() != null ? policyDetails.getApplicableLines().trim() : null);
policy.setStartTime(policyDetails.getStartTime());
policy.setEndTime(policyDetails.getEndTime());
policy.setEnabled(policyDetails.isEnabled());
policy.setUpdatedAt(LocalDateTime.now());
DiscountPolicy saved = discountPolicyRepository.save(policy);
adminOperationLogger.log("POLICY_UPDATE", "POLICY", saved.getId(), "update policy", true, null);
return saved;
}
adminOperationLogger.log("POLICY_UPDATE", "POLICY", policyId, "update policy", false, "policy not found");
return null;
}
public boolean enablePolicy(Long policyId) {
Optional<DiscountPolicy> policyOpt = discountPolicyRepository.findById(policyId);
if (policyOpt.isPresent()) {
DiscountPolicy policy = policyOpt.get();
policy.setEnabled(true);
policy.setUpdatedAt(LocalDateTime.now());
discountPolicyRepository.save(policy);
adminOperationLogger.log("POLICY_ENABLE", "POLICY", policyId, "enable policy", true, null);
return true;
}
adminOperationLogger.log("POLICY_ENABLE", "POLICY", policyId, "enable policy", false, "policy not found");
return false;
}
public boolean disablePolicy(Long policyId) {
Optional<DiscountPolicy> policyOpt = discountPolicyRepository.findById(policyId);
if (policyOpt.isPresent()) {
DiscountPolicy policy = policyOpt.get();
policy.setEnabled(false);
policy.setUpdatedAt(LocalDateTime.now());
discountPolicyRepository.save(policy);
adminOperationLogger.log("POLICY_DISABLE", "POLICY", policyId, "disable policy", true, null);
return true;
}
adminOperationLogger.log("POLICY_DISABLE", "POLICY", policyId, "disable policy", false, "policy not found");
return false;
}
public boolean deletePolicy(Long policyId) {
if (discountPolicyRepository.existsById(policyId)) {
discountPolicyRepository.deleteById(policyId);
adminOperationLogger.log("POLICY_DELETE", "POLICY", policyId, "delete policy", true, null);
return true;
}
adminOperationLogger.log("POLICY_DELETE", "POLICY", policyId, "delete policy", false, "policy not found");
return false;
}
public List<DiscountPolicy> getPoliciesByLine(String line) {
return discountPolicyRepository.findByApplicableLinesContaining(line);
}
}

@ -1,97 +0,0 @@
package com.example.alipay.service.admin;
import com.example.alipay.model.fintech.User;
import com.example.alipay.model.fintech.UserAudit;
import com.example.alipay.model.fintech.UserStatus;
import com.example.alipay.repository.fintech.UserAuditRepository;
import com.example.alipay.repository.fintech.UserRepository;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
public class UserManagementService {
private final UserRepository userRepository;
private final UserAuditRepository userAuditRepository;
private final AdminOperationLogger adminOperationLogger;
public UserManagementService(UserRepository userRepository, UserAuditRepository userAuditRepository, AdminOperationLogger adminOperationLogger) {
this.userRepository = userRepository;
this.userAuditRepository = userAuditRepository;
this.adminOperationLogger = adminOperationLogger;
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public Optional<User> getUserById(Long userId) {
return userRepository.findById(userId);
}
public boolean disableUser(Long userId) {
Optional<User> userOpt = userRepository.findById(userId);
if (userOpt.isPresent()) {
User user = userOpt.get();
user.setStatus(UserStatus.INACTIVE);
userRepository.save(user);
adminOperationLogger.log("USER_DISABLE", "USER", userId, "disable user", true, null);
return true;
}
adminOperationLogger.log("USER_DISABLE", "USER", userId, "disable user", false, "user not found");
return false;
}
public boolean enableUser(Long userId) {
Optional<User> userOpt = userRepository.findById(userId);
if (userOpt.isPresent()) {
User user = userOpt.get();
user.setStatus(UserStatus.ACTIVE);
userRepository.save(user);
adminOperationLogger.log("USER_ENABLE", "USER", userId, "enable user", true, null);
return true;
}
adminOperationLogger.log("USER_ENABLE", "USER", userId, "enable user", false, "user not found");
return false;
}
public boolean approveUserAudit(Long userId) {
Optional<UserAudit> auditOpt = userAuditRepository.findByUserId(userId);
Optional<User> userOpt = userRepository.findById(userId);
if (auditOpt.isPresent() && userOpt.isPresent()) {
User user = userOpt.get();
user.setStatus(UserStatus.ACTIVE);
userRepository.save(user);
userAuditRepository.delete(auditOpt.get());
adminOperationLogger.log("USER_AUDIT_APPROVE", "USER", userId, "approve user audit", true, null);
return true;
}
adminOperationLogger.log("USER_AUDIT_APPROVE", "USER", userId, "approve user audit", false, "audit or user not found");
return false;
}
public boolean rejectUserAudit(Long userId, String rejectReason) {
Optional<UserAudit> auditOpt = userAuditRepository.findByUserId(userId);
Optional<User> userOpt = userRepository.findById(userId);
if (auditOpt.isPresent() && userOpt.isPresent()) {
User user = userOpt.get();
user.setStatus(UserStatus.REJECTED);
userRepository.save(user);
UserAudit audit = auditOpt.get();
audit.setRejectReason(rejectReason);
userAuditRepository.save(audit);
adminOperationLogger.log("USER_AUDIT_REJECT", "USER", userId, "reject user audit: " + rejectReason, true, null);
return true;
}
adminOperationLogger.log("USER_AUDIT_REJECT", "USER", userId, "reject user audit", false, "audit or user not found");
return false;
}
public List<UserAudit> getPendingAudits() {
// 获取待审核的用户
return userAuditRepository.findAll();
}
}

@ -1,94 +0,0 @@
package com.example.alipay.service.fintech;
import com.example.alipay.model.fintech.BankCard;
import com.example.alipay.model.fintech.Bill;
import com.example.alipay.model.fintech.User;
import com.example.alipay.repository.fintech.BankCardRepository;
import com.example.alipay.repository.fintech.BillRepository;
import com.example.alipay.repository.fintech.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
public class AssetService {
private final UserRepository userRepository;
private final BankCardRepository bankCardRepository;
private final BillRepository billRepository;
public AssetService(UserRepository userRepository, BankCardRepository bankCardRepository, BillRepository billRepository) {
this.userRepository = userRepository;
this.bankCardRepository = bankCardRepository;
this.billRepository = billRepository;
}
public BigDecimal getBalance(Long userId) {
Optional<User> userOpt = userRepository.findById(userId);
return userOpt.map(User::getBalance).orElse(BigDecimal.ZERO);
}
public BankCard addBankCard(Long userId, String bankName, String cardNumber, Boolean isDefault) {
BankCard card = new BankCard();
card.setUserId(userId);
card.setBankName(bankName);
card.setCardNumber(cardNumber);
card.setIsDefault(isDefault);
if (isDefault) {
List<BankCard> defaultCards = bankCardRepository.findByUserIdAndIsDefaultTrue(userId);
for (BankCard defaultCard : defaultCards) {
defaultCard.setIsDefault(false);
bankCardRepository.save(defaultCard);
}
}
return bankCardRepository.save(card);
}
public List<BankCard> getUserCards(Long userId) {
return bankCardRepository.findByUserId(userId);
}
@Transactional
public boolean transfer(Long fromUserId, Long toUserId, BigDecimal amount) {
Optional<User> fromUserOpt = userRepository.findByIdForUpdate(fromUserId);
Optional<User> toUserOpt = userRepository.findByIdForUpdate(toUserId);
if (fromUserOpt.isEmpty() || toUserOpt.isEmpty()) {
return false;
}
User fromUser = fromUserOpt.get();
User toUser = toUserOpt.get();
if (fromUser.getBalance().compareTo(amount) < 0) {
return false;
}
fromUser.setBalance(fromUser.getBalance().subtract(amount));
userRepository.save(fromUser);
toUser.setBalance(toUser.getBalance().add(amount));
userRepository.save(toUser);
createBill(fromUserId, amount.negate(), "EXPENDITURE", "转账", "转账给用户" + toUser.getUsername());
createBill(toUserId, amount, "INCOME", "转账", "收到用户" + fromUser.getUsername() + "的转账");
return true;
}
private void createBill(Long userId, BigDecimal amount, String type, String category, String remark) {
Bill bill = new Bill();
bill.setUserId(userId);
bill.setAmount(amount);
bill.setType(type);
bill.setCategory(category);
bill.setTransactionTime(LocalDateTime.now());
bill.setRemark(remark);
billRepository.save(bill);
}
}

@ -1,72 +0,0 @@
package com.example.alipay.service.fintech;
import com.example.alipay.model.fintech.User;
import com.example.alipay.model.fintech.UserStatus;
import com.example.alipay.model.fintech.UserAudit;
import com.example.alipay.repository.fintech.UserRepository;
import com.example.alipay.repository.fintech.UserAuditRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Optional;
@Service
public class AuthService {
private final UserRepository userRepository;
private final UserAuditRepository userAuditRepository;
private final PasswordEncoder passwordEncoder;
public AuthService(UserRepository userRepository, UserAuditRepository userAuditRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.userAuditRepository = userAuditRepository;
this.passwordEncoder = passwordEncoder;
}
public User register(String username, String password, String phone, String realName, String idCardNumber) {
if (userRepository.existsByUsername(username)) {
throw new RuntimeException("用户名已存在");
}
User user = User.builder()
.username(username)
.password(passwordEncoder.encode(password))
.phone(phone)
.balance(new BigDecimal("0.00"))
.status(UserStatus.PENDING_AUDIT)
.build();
user = userRepository.save(user);
UserAudit audit = new UserAudit();
audit.setUserId(user.getId());
audit.setRealName(realName);
audit.setIdCardNumber(idCardNumber);
audit.setSubmitTime(java.time.LocalDateTime.now());
userAuditRepository.save(audit);
return user;
}
public Optional<User> authenticate(String username, String password) {
Optional<User> userOpt = userRepository.findByUsername(username);
if (userOpt.isPresent() && passwordEncoder.matches(password, userOpt.get().getPassword())) {
return userOpt;
}
return Optional.empty();
}
public boolean setPayPassword(Long userId, String newPassword, String oldPassword, String requesterUsername) {
Optional<User> userOpt = userRepository.findById(userId);
if (userOpt.isEmpty()) return false;
User user = userOpt.get();
if (!user.getUsername().equals(requesterUsername)) return false;
if (oldPassword != null && user.getPayPassword() != null) {
if (!passwordEncoder.matches(oldPassword, user.getPayPassword())) return false;
}
user.setPayPassword(passwordEncoder.encode(newPassword));
user.setPayPwdFailedAttempts(0);
user.setPayPwdLockedUntil(null);
userRepository.save(user);
return true;
}
}

@ -1,93 +0,0 @@
package com.example.alipay.service.fintech;
import com.example.alipay.model.fintech.Bill;
import com.example.alipay.repository.fintech.BillRepository;
import com.example.alipay.repository.fintech.UserRepository;
import lombok.Data;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class BillService {
private final BillRepository billRepository;
private final UserRepository userRepository;
public BillService(BillRepository billRepository, UserRepository userRepository) {
this.billRepository = billRepository;
this.userRepository = userRepository;
}
public List<Bill> getUserBills(Long userId) {
return billRepository.findByUserIdOrderByTransactionTimeDesc(userId);
}
public List<Bill> getUserBillsByType(Long userId, String type) {
return billRepository.findByUserIdAndTypeOrderByTransactionTimeDesc(userId, type);
}
public BillOverviewDto getBillOverview(Long userId) {
List<Bill> bills = getUserBills(userId);
BigDecimal totalIncome = bills.stream()
.filter(bill -> "INCOME".equals(bill.getType()))
.map(Bill::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal totalExpenditure = bills.stream()
.filter(bill -> "EXPENDITURE".equals(bill.getType()))
.map(Bill::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Map<String, BigDecimal> categoryExpenditure = bills.stream()
.filter(bill -> "EXPENDITURE".equals(bill.getType()))
.collect(Collectors.groupingBy(
Bill::getCategory,
Collectors.reducing(BigDecimal.ZERO, Bill::getAmount, BigDecimal::add)
));
BillOverviewDto overview = new BillOverviewDto();
overview.setTotalIncome(totalIncome);
overview.setTotalExpenditure(totalExpenditure);
overview.setCategoryExpenditure(categoryExpenditure);
overview.setBillCount(bills.size());
return overview;
}
public Bill createBill(Long userId, BigDecimal amount, String type, String category, String remark) {
Bill bill = new Bill();
bill.setUserId(userId);
bill.setAmount(amount);
bill.setType(type);
bill.setCategory(category);
bill.setTransactionTime(LocalDateTime.now());
bill.setRemark(remark);
bill = billRepository.save(bill);
userRepository.findById(userId).ifPresent(u -> {
BigDecimal absAmt = amount == null ? BigDecimal.ZERO : amount.abs();
BigDecimal current = u.getBalance() == null ? BigDecimal.ZERO : u.getBalance();
if ("INCOME".equalsIgnoreCase(type)) {
u.setBalance(current.add(absAmt));
} else if ("EXPENDITURE".equalsIgnoreCase(type)) {
u.setBalance(current.subtract(absAmt));
}
userRepository.save(u);
});
return bill;
}
@Data
public static class BillOverviewDto {
private BigDecimal totalIncome;
private BigDecimal totalExpenditure;
private Map<String, BigDecimal> categoryExpenditure;
private Integer billCount;
}
}

@ -1,375 +0,0 @@
package com.example.alipay.service.site;
import com.example.alipay.model.admin.DiscountPolicy;
import com.example.alipay.model.TravelRecord;
import com.example.alipay.model.site.Gate;
import com.example.alipay.model.site.GateEvent;
import com.example.alipay.model.site.Station;
import com.example.alipay.model.fintech.User;
import com.example.alipay.repository.admin.DiscountPolicyRepository;
import com.example.alipay.repository.TravelRecordRepository;
import com.example.alipay.repository.site.GateRepository;
import com.example.alipay.repository.site.GateEventRepository;
import com.example.alipay.repository.site.StationRepository;
import com.example.alipay.repository.fintech.UserRepository;
import com.example.alipay.model.fintech.Bill;
import com.example.alipay.repository.fintech.BillRepository;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Service
public class GateEventService {
private final GateRepository gateRepository;
private final GateEventRepository gateEventRepository;
private final UserRepository userRepository;
private final BillRepository billRepository;
private final StationRepository stationRepository;
private final DiscountPolicyRepository discountPolicyRepository;
private final TravelRecordRepository travelRecordRepository;
public GateEventService(
GateRepository gateRepository,
GateEventRepository gateEventRepository,
UserRepository userRepository,
BillRepository billRepository,
StationRepository stationRepository,
DiscountPolicyRepository discountPolicyRepository,
TravelRecordRepository travelRecordRepository
) {
this.gateRepository = gateRepository;
this.gateEventRepository = gateEventRepository;
this.userRepository = userRepository;
this.billRepository = billRepository;
this.stationRepository = stationRepository;
this.discountPolicyRepository = discountPolicyRepository;
this.travelRecordRepository = travelRecordRepository;
}
public GateEvent processEntry(String gateCode, String qrCode, Long stationId) {
Optional<Gate> gateOpt = resolveGateByCode(gateCode, stationId);
if (gateOpt.isEmpty()) {
return createErrorEvent(gateCode, qrCode, "GATE_NOT_FOUND", "闸机不存在");
}
Gate gate = gateOpt.get();
if (!gate.isEnabled() || !"ONLINE".equals(gate.getStatus())) {
return createErrorEvent(gateCode, qrCode, "GATE_OFFLINE", "闸机离线");
}
if (!"ENTRY".equals(gate.getDirection())) {
return createErrorEvent(gateCode, qrCode, "INVALID_DIRECTION", "此闸机不是进站口");
}
// 解析二维码获取用户信息
Optional<User> userOpt = parseUserFromQRCode(qrCode);
if (userOpt.isEmpty()) {
return createErrorEvent(gateCode, qrCode, "INVALID_QRCODE", "无效的二维码");
}
User user = userOpt.get();
// 检查用户状态
if (!user.isEnabled()) {
return createErrorEvent(gateCode, qrCode, "USER_DISABLED", "用户账户已禁用");
}
// 检查余额
if (user.getBalance().compareTo(new BigDecimal("2.00")) < 0) {
return createErrorEvent(gateCode, qrCode, "INSUFFICIENT_BALANCE", "余额不足");
}
// 创建进站事件
GateEvent event = new GateEvent();
event.setGateId(gate.getId());
event.setGateCode(gateCode);
event.setUserId(user.getId());
event.setUsername(user.getUsername());
event.setEventType("ENTRY");
event.setQrCode(qrCode);
event.setFare(BigDecimal.ZERO); // 进站不扣费
event.setStatus("SUCCESS");
event.setEventTime(LocalDateTime.now());
event.setProcessedTime(LocalDateTime.now());
event.setTransactionId(generateTransactionId());
event.setRemark("进站成功");
GateEvent saved = gateEventRepository.save(event);
Station entryStation = resolveStation(gate.getStationId()).orElse(null);
upsertEntryRecord(user.getUsername(), entryStation);
return saved;
}
public GateEvent processExit(String gateCode, String qrCode, Long stationId) {
Optional<Gate> gateOpt = resolveGateByCode(gateCode, stationId);
if (gateOpt.isEmpty()) {
return createErrorEvent(gateCode, qrCode, "GATE_NOT_FOUND", "闸机不存在");
}
Gate gate = gateOpt.get();
if (!gate.isEnabled() || !"ONLINE".equals(gate.getStatus())) {
return createErrorEvent(gateCode, qrCode, "GATE_OFFLINE", "闸机离线");
}
if (!"EXIT".equals(gate.getDirection())) {
return createErrorEvent(gateCode, qrCode, "INVALID_DIRECTION", "此闸机不是出站口");
}
// 解析二维码获取用户信息
Optional<User> userOpt = parseUserFromQRCode(qrCode);
if (userOpt.isEmpty()) {
return createErrorEvent(gateCode, qrCode, "INVALID_QRCODE", "无效的二维码");
}
User user = userOpt.get();
// 检查用户状态
if (!user.isEnabled()) {
return createErrorEvent(gateCode, qrCode, "USER_DISABLED", "用户账户已禁用");
}
Optional<GateEvent> latestEntryOpt = gateEventRepository.findTopByUserIdAndEventTypeAndStatusOrderByEventTimeDesc(
user.getId(),
"ENTRY",
"SUCCESS"
);
if (latestEntryOpt.isEmpty()) {
return createErrorEvent(gateCode, qrCode, "NO_ENTRY_RECORD", "未找到进站记录");
}
Optional<GateEvent> latestExitOpt = gateEventRepository.findTopByUserIdAndEventTypeAndStatusOrderByEventTimeDesc(
user.getId(),
"EXIT",
"SUCCESS"
);
if (latestExitOpt.isPresent() && latestExitOpt.get().getEventTime() != null && latestEntryOpt.get().getEventTime() != null) {
if (latestExitOpt.get().getEventTime().isAfter(latestEntryOpt.get().getEventTime())) {
return createErrorEvent(gateCode, qrCode, "NO_ENTRY_RECORD", "未找到未出站的进站记录");
}
}
GateEvent latestEntry = latestEntryOpt.get();
Optional<Gate> entryGateOpt = latestEntry.getGateId() == null ? Optional.empty() : gateRepository.findById(latestEntry.getGateId());
if (entryGateOpt.isEmpty()) {
return createErrorEvent(gateCode, qrCode, "ENTRY_GATE_NOT_FOUND", "进站闸机不存在");
}
Gate entryGate = entryGateOpt.get();
Station entryStation = resolveStation(entryGate.getStationId()).orElse(null);
if (entryStation == null) {
return createErrorEvent(gateCode, qrCode, "ENTRY_STATION_NOT_FOUND", "进站站点不存在");
}
Station exitStation = resolveStation(gate.getStationId()).orElse(null);
if (exitStation == null) {
return createErrorEvent(gateCode, qrCode, "EXIT_STATION_NOT_FOUND", "出站站点不存在");
}
if (entryStation.getLine() == null || exitStation.getLine() == null || !entryStation.getLine().equals(exitStation.getLine())) {
return createErrorEvent(gateCode, qrCode, "CROSS_LINE_NOT_SUPPORTED", "暂不支持跨线路计费");
}
Integer entrySeq = parseStationSequence(entryStation.getLocation());
Integer exitSeq = parseStationSequence(exitStation.getLocation());
if (entrySeq == null || exitSeq == null) {
return createErrorEvent(gateCode, qrCode, "INVALID_STATION_SEQUENCE", "站点位置字段必须为数字顺序");
}
int stationCount = Math.abs(exitSeq - entrySeq);
BigDecimal perStationFare = new BigDecimal("3.00");
BigDecimal fare = perStationFare.multiply(new BigDecimal(stationCount));
Optional<DiscountPolicy> bestPolicyOpt = findBestPolicy(entryStation.getLine(), LocalDateTime.now());
if (bestPolicyOpt.isPresent()) {
BigDecimal rate = bestPolicyOpt.get().getDiscountRate();
if (rate != null) {
fare = fare.multiply(rate).setScale(2, RoundingMode.HALF_UP);
}
} else {
fare = fare.setScale(2, RoundingMode.HALF_UP);
}
// 检查余额
if (user.getBalance().compareTo(fare) < 0) {
return createErrorEvent(gateCode, qrCode, "INSUFFICIENT_BALANCE", "余额不足");
}
// 扣费
user.setBalance(user.getBalance().subtract(fare));
userRepository.save(user);
GateEvent event = new GateEvent();
event.setGateId(gate.getId());
event.setGateCode(gateCode);
event.setUserId(user.getId());
event.setUsername(user.getUsername());
event.setEventType("EXIT");
event.setQrCode(qrCode);
event.setFare(fare);
event.setStatus("SUCCESS");
event.setEventTime(LocalDateTime.now());
event.setProcessedTime(LocalDateTime.now());
event.setTransactionId(generateTransactionId());
event.setRemark("出站成功,扣费" + fare + "元");
event = gateEventRepository.save(event);
String ref = event.getTransactionId();
if (!billRepository.existsByUserIdAndSourceTypeAndSourceRefId(user.getId(), "GATE", ref)) {
Bill b = new Bill();
b.setUserId(user.getId());
b.setAmount(fare);
b.setType("EXPENDITURE");
b.setCategory("交通");
b.setTransactionTime(LocalDateTime.now());
b.setRemark("出站扣费");
b.setSourceType("GATE");
b.setSourceRefId(ref);
billRepository.save(b);
}
upsertExitRecord(user.getUsername(), entryStation, fare);
return event;
}
private Optional<Gate> resolveGateByCode(String gateCode, Long stationId) {
if (gateCode == null || gateCode.isBlank()) return Optional.empty();
if (stationId != null) {
Optional<Gate> stationEnabled = gateRepository.findTopByStationIdAndGateCodeAndEnabledTrueOrderByUpdatedAtDesc(stationId, gateCode);
if (stationEnabled.isPresent()) return stationEnabled;
Optional<Gate> stationAny = gateRepository.findTopByStationIdAndGateCodeOrderByUpdatedAtDesc(stationId, gateCode);
if (stationAny.isPresent()) return stationAny;
}
Optional<Gate> enabled = gateRepository.findTopByGateCodeAndEnabledTrueOrderByUpdatedAtDesc(gateCode);
if (enabled.isPresent()) return enabled;
return gateRepository.findTopByGateCodeOrderByUpdatedAtDesc(gateCode);
}
private Optional<Station> resolveStation(Long stationId) {
if (stationId == null) return Optional.empty();
return stationRepository.findById(stationId);
}
private void upsertEntryRecord(String username, Station entryStation) {
if (username == null || username.isBlank()) return;
Optional<TravelRecord> existing = travelRecordRepository.findTopByUsernameAndStatusOrderByEntryTimeDesc(username, "IN_PROGRESS");
if (existing.isPresent()) return;
TravelRecord r = new TravelRecord();
r.setUsername(username);
if (entryStation != null) {
r.setCity(entryStation.getCity());
r.setLine(entryStation.getLine());
}
r.setEntryTime(LocalDateTime.now());
r.setStatus("IN_PROGRESS");
travelRecordRepository.save(r);
}
private void upsertExitRecord(String username, Station entryStation, BigDecimal fare) {
if (username == null || username.isBlank()) return;
Optional<TravelRecord> inProgress = travelRecordRepository.findTopByUsernameAndStatusOrderByEntryTimeDesc(username, "IN_PROGRESS");
TravelRecord r = inProgress.orElseGet(TravelRecord::new);
if (r.getUsername() == null) r.setUsername(username);
if (r.getEntryTime() == null) r.setEntryTime(LocalDateTime.now());
if (r.getCity() == null && entryStation != null) r.setCity(entryStation.getCity());
if (r.getLine() == null && entryStation != null) r.setLine(entryStation.getLine());
r.setExitTime(LocalDateTime.now());
r.setFare(fare);
r.setStatus("COMPLETED");
travelRecordRepository.save(r);
}
private Integer parseStationSequence(String location) {
if (location == null) return null;
String s = location.trim();
if (s.isEmpty()) return null;
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return null;
}
}
private Optional<DiscountPolicy> findBestPolicy(String line, LocalDateTime now) {
List<DiscountPolicy> candidates = discountPolicyRepository.findByEnabledTrue();
DiscountPolicy best = null;
for (DiscountPolicy p : candidates) {
if (p == null) continue;
if (p.getDiscountRate() == null) continue;
if (!isPolicyInTimeWindow(p, now)) continue;
if (!isPolicyApplicableToLine(p, line)) continue;
if (best == null || p.getDiscountRate().compareTo(best.getDiscountRate()) < 0) {
best = p;
}
}
return Optional.ofNullable(best);
}
private boolean isPolicyInTimeWindow(DiscountPolicy p, LocalDateTime now) {
if (now == null) return true;
if (p.getStartTime() != null && now.isBefore(p.getStartTime())) return false;
if (p.getEndTime() != null && now.isAfter(p.getEndTime())) return false;
return true;
}
private boolean isPolicyApplicableToLine(DiscountPolicy p, String line) {
if (line == null || line.isBlank()) return true;
String lines = p.getApplicableLines();
if (lines == null || lines.isBlank()) return true;
return lines.contains(line);
}
private GateEvent createErrorEvent(String gateCode, String qrCode, String errorCode, String errorMessage) {
GateEvent event = new GateEvent();
event.setGateCode(gateCode);
event.setQrCode(qrCode);
event.setEventType("ERROR");
event.setStatus("FAILED");
event.setErrorCode(errorCode);
event.setErrorMessage(errorMessage);
event.setEventTime(LocalDateTime.now());
event.setProcessedTime(LocalDateTime.now());
event.setTransactionId(generateTransactionId());
event.setRemark("处理失败: " + errorMessage);
return gateEventRepository.save(event);
}
private Optional<User> parseUserFromQRCode(String qrCode) {
// 简化逻辑假设二维码包含用户ID
try {
Long userId = Long.parseLong(qrCode);
return userRepository.findById(userId);
} catch (NumberFormatException e) {
return Optional.empty();
}
}
private String generateTransactionId() {
return "TXN_" + UUID.randomUUID().toString().replace("-", "").substring(0, 16);
}
public List<GateEvent> getGateEvents(Long gateId) {
return gateEventRepository.findByGateIdOrderByEventTimeDesc(gateId);
}
public List<GateEvent> getUserEvents(Long userId) {
return gateEventRepository.findByUserIdOrderByEventTimeDesc(userId);
}
public List<GateEvent> getErrorEvents() {
return gateEventRepository.findByStatusOrderByEventTimeDesc("FAILED");
}
}

@ -1,166 +0,0 @@
package com.example.alipay.service.site;
import com.example.alipay.model.site.Station;
import com.example.alipay.model.site.Gate;
import com.example.alipay.repository.site.StationRepository;
import com.example.alipay.repository.site.GateRepository;
import com.example.alipay.repository.site.GateEventRepository;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
public class StationManagementService {
private final StationRepository stationRepository;
private final GateRepository gateRepository;
private final GateEventRepository gateEventRepository;
public StationManagementService(StationRepository stationRepository, GateRepository gateRepository, GateEventRepository gateEventRepository) {
this.stationRepository = stationRepository;
this.gateRepository = gateRepository;
this.gateEventRepository = gateEventRepository;
}
// 站点管理方法
public List<Station> getAllStations() {
return stationRepository.findAll();
}
public List<Station> getActiveStations() {
return stationRepository.findByEnabledTrue();
}
public Optional<Station> getStationById(Long stationId) {
return stationRepository.findById(stationId);
}
public Optional<Station> getStationByCode(String stationCode) {
return stationRepository.findByStationCode(stationCode);
}
public Station createStation(Station station) {
station.setCreatedAt(LocalDateTime.now());
station.setUpdatedAt(LocalDateTime.now());
return stationRepository.save(station);
}
public Station updateStation(Long stationId, Station stationDetails) {
Optional<Station> stationOpt = stationRepository.findById(stationId);
if (stationOpt.isPresent()) {
Station station = stationOpt.get();
station.setStationName(stationDetails.getStationName());
station.setCity(stationDetails.getCity());
station.setLine(stationDetails.getLine());
station.setLocation(stationDetails.getLocation());
station.setDescription(stationDetails.getDescription());
station.setEnabled(stationDetails.isEnabled());
station.setUpdatedAt(LocalDateTime.now());
return stationRepository.save(station);
}
return null;
}
public boolean enableStation(Long stationId) {
Optional<Station> stationOpt = stationRepository.findById(stationId);
if (stationOpt.isPresent()) {
Station station = stationOpt.get();
station.setEnabled(true);
station.setUpdatedAt(LocalDateTime.now());
stationRepository.save(station);
return true;
}
return false;
}
public boolean disableStation(Long stationId) {
Optional<Station> stationOpt = stationRepository.findById(stationId);
if (stationOpt.isPresent()) {
Station station = stationOpt.get();
station.setEnabled(false);
station.setUpdatedAt(LocalDateTime.now());
stationRepository.save(station);
return true;
}
return false;
}
// 闸机管理方法
public List<Gate> getGatesByStation(Long stationId) {
return gateRepository.findByStationId(stationId);
}
public List<Gate> getGatesByDirection(String direction) {
return gateRepository.findByDirection(direction);
}
public Gate createGate(Gate gate) {
gate.setCreatedAt(LocalDateTime.now());
gate.setUpdatedAt(LocalDateTime.now());
return gateRepository.save(gate);
}
public Gate updateGate(Long gateId, Gate gateDetails) {
Optional<Gate> gateOpt = gateRepository.findById(gateId);
if (gateOpt.isPresent()) {
Gate gate = gateOpt.get();
gate.setGateName(gateDetails.getGateName());
gate.setDirection(gateDetails.getDirection());
gate.setLocation(gateDetails.getLocation());
gate.setDescription(gateDetails.getDescription());
gate.setEnabled(gateDetails.isEnabled());
gate.setStatus(gateDetails.getStatus());
gate.setUpdatedAt(LocalDateTime.now());
return gateRepository.save(gate);
}
return null;
}
public boolean enableGate(Long gateId) {
Optional<Gate> gateOpt = gateRepository.findById(gateId);
if (gateOpt.isPresent()) {
Gate gate = gateOpt.get();
gate.setEnabled(true);
gate.setStatus("ONLINE");
gate.setUpdatedAt(LocalDateTime.now());
gateRepository.save(gate);
return true;
}
return false;
}
public boolean disableGate(Long gateId) {
Optional<Gate> gateOpt = gateRepository.findById(gateId);
if (gateOpt.isPresent()) {
Gate gate = gateOpt.get();
gate.setEnabled(false);
gate.setStatus("OFFLINE");
gate.setUpdatedAt(LocalDateTime.now());
gateRepository.save(gate);
return true;
}
return false;
}
public boolean deleteGate(Long gateId) {
Optional<Gate> gateOpt = gateRepository.findById(gateId);
if (gateOpt.isEmpty()) {
return false;
}
long related = gateEventRepository.countByGateId(gateId);
if (related > 0) {
throw new IllegalStateException("闸机存在关联事件,无法删除");
}
gateRepository.deleteById(gateId);
return true;
}
public List<Station> getStationsByCity(String city) {
return stationRepository.findByCity(city);
}
public List<Station> getStationsByLine(String line) {
return stationRepository.findByLine(line);
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save