业务背景先放一边,直接聊聊这个系统本身。“springboot各银行金融理财产品推荐系统vue”这种题目,最近在毕业设计和中小型企业内部工具里出现的频率非常高。核心诉求其实很一致:用一套相对标准的Web技术栈,把银行理财产品的展示、筛选、推荐、用户风险测评这些业务串起来。前端用Vue做交互,后端用Spring Boot提供接口,数据落库,前后端通过JSON交互,典型的互联网公司标准开发模式。
这类项目最容易被误解的地方是“推荐”两个字。很多人一听推荐系统,就觉得要上机器学习、协同过滤、深度学习那一套,实际上对于银行理财产品推荐这种场景,首选方案从来不是算法,而是规则引擎加多维度打分排序。原因很简单:金融产品推荐对可解释性要求极高,客户经理需要能说清楚“为什么推荐这个产品”,监管也要求推荐逻辑透明。黑盒模型在这个场景里没有生存空间。
这篇文章我会完整拆解这个系统的设计思路、表结构、核心接口、推荐策略,以及我在实际开发中踩过的坑。无论你是要做毕业设计,还是公司内部要做一个类似的产品货架,这套东西都能直接参考。
1. 系统设计与模块拆分逻辑
1.1 用户、产品、推荐三条主线的划分
整个系统拆成三大块:用户中心、产品中心、推荐中心。这三条线各管各的,接口层面互相调用但不耦合。
用户中心负责注册、登录、风险测评问卷、个人信息维护。风险测评是金融理财系统的刚需功能,监管要求必须对客户进行风险承受能力评估,然后才能销售相应风险等级的产品。这块做不好,系统上线第一天就会被合规部门打回来。
产品中心负责理财产品的CRUD管理,包括产品名称、代码、发行银行、预期收益率、风险等级、起购金额、产品期限、募集开始结束时间、产品状态等字段。管理员通过后台维护这些数据,前端通过分页、条件筛选接口获取列表。
推荐中心是整个系统的核心亮点。它做的事情是:根据用户的风险测评结果、资产状况、历史购买行为(如果有),从在售产品中筛选出符合条件的产品,按照收益率、期限、起购金额等多个维度加权打分,最终输出一个排序后的推荐列表。
1.2 为什么推荐逻辑要独立成服务
很多初学者喜欢把推荐逻辑写在Controller里,也就是用户请求进来之后,直接在接口方法里写筛选条件,循环遍历产品列表,然后返回结果。这种方式在小数据量下看起来能跑,但一旦产品数量过万、用户量上来,接口响应时间会直线上升,而且推荐规则一变就要改Controller代码,非常痛苦。
我的做法是把推荐逻辑单独抽成一个RecommendService,内部使用策略模式。每种推荐策略实现同一个接口,例如recommendByRiskLevel(按风险等级推荐)、recommendByAssetsAndTerm(按资产和期限推荐)、recommendByHotSales(按热度推荐),然后根据用户特征动态选择一种或组合多种策略。
这样做的好处是:规则可以独立测试、独立优化、独立部署,而且后续如果要接实时计算引擎或者算法模型,只需要替换底层实现,对上层接口完全透明。架构上做到了“面向扩展开放,面向修改封闭”。
1.3 数据库表结构设计的关键点
表结构设计上,我用了五张核心表:
user:用户表,字段包括id、username、password、phone、risk_level、total_assets等。product:理财产品表,字段包括id、product_code、bank_name、product_name、expected_return、risk_level、min_purchase_amount、term_days、status、sale_start_time、sale_end_time。risk_assessment_question:风险测评题目表。risk_assessment_record:用户测评记录表,存每次测评的答案和结果。user_favorite:用户收藏表,记录用户关注过的产品。
这里有一个重要的设计细节:风险测评记录表不仅要存测评结果风险等级,还要存当时的答案明细。因为用户可能会对测评结果提出异议,这时候需要能够回溯,确认当时的答题情况是否准确。我用一个JSON字段(比如MySQL的json类型)存储答题明细,既灵活又方便回溯。
产品表的status字段建议用int类型,0表示下架,1表示在售,2表示即将开售,3表示已售罄。不要用字符串“在售”“下架”这样的中文值,因为产品经理可能随时改名,到时候update语句写到你怀疑人生。用数字字典,前端翻译显示名称,这才是正规做法。
2. 推荐策略的核心实现:加权打分模型
2.1 风险等级匹配是硬性门槛
银行理财产品的风险等级通常分为R1(谨慎型)、R2(稳健型)、R3(平衡型)、R4(进取型)、R5(激进型)。用户的风险测评结果决定了他最多能买哪个等级的产品。
这个规则在推荐逻辑里是硬性过滤条件,不是加分项。也就是说,如果用户测出来是R2,那么R3及以上的产品直接过滤掉,不管收益率多高都不推。这不仅是业务逻辑,更是合规要求。销售风险等级不匹配的产品给用户,就是违规。
代码实现如下:
public List<Product> filterByRiskLevel(List<Product> products, String userRiskLevel) { int userLevel = RiskLevelEnum.valueOf(userRiskLevel).getLevel(); return products.stream() .filter(product -> { int productLevel = RiskLevelEnum.valueOf(product.getRiskLevel()).getLevel(); return productLevel <= userLevel; }) .collect(Collectors.toList()); }RiskLevelEnum就是R1到R5对应1到5的枚举,比较逻辑一目了然。
2.2 加权评分公式的设计与调参
通过了风险等级这个硬性门槛之后,剩下的产品需要打分排序。我用的评分公式是:
score = expectedReturnScore * 0.4 + termScore * 0.3 + minAmountScore * 0.2 + bankReputationScore * 0.1这四个子分数都是标准化到0到100之间的相对得分,而不是直接用原始数值。为什么要标准化?因为收益率可能是4.5%,起购金额可能是10000元,期限可能是180天,数量级完全不同,直接相加毫无意义。
标准化的方式采用线性归一化,也就是把当前候选产品集里的最大值映射到100,最小值映射到0,其他值按比例分布:
public double normalize(double value, double min, double max) { if (max == min) { return 100.0; } return (value - min) / (max - min) * 100; }这里有一个细节需要注意:归一化的基准应该是当前候选集,而不是全量产品表。因为用户看推荐列表时,他关心的是“这批产品里哪个相对更好”,而不是“这个产品在全行业里排第几”。用候选集做基准,计算简单,而且排序效果直观。
加权系数的选择,我默认是收益率40%、期限30%、起购金额20%、银行信誉10%。这个权重不是拍脑袋定的,是根据用户调研问卷统计出来的。实际开发中可以做成动态配置,放到数据库或者配置中心里,方便运营人员调整。比如某个阶段银行主推长期产品,就可以把期限的权重调高。
2.3 完整推荐代码示例
@Service public class ProductRecommendService { @Autowired private ProductMapper productMapper; @Autowired private RiskAssessmentMapper riskAssessmentMapper; public List<ProductVO> recommend(Long userId, int page, int pageSize) { // 1. 获取用户风险等级 String userRiskLevel = riskAssessmentMapper.findLatestLevelByUserId(userId); if (userRiskLevel == null) { throw new BusinessException("请先完成风险测评"); } // 2. 获取在售产品 List<Product> allProducts = productMapper.findByStatus(ProductStatus.ON_SALE.getCode()); // 3. 风险等级硬性过滤 List<Product> candidate = filterByRiskLevel(allProducts, userRiskLevel); // 4. 计算打分并排序 List<ProductScore> scored = candidate.stream() .map(p -> buildProductScore(p, candidate)) .sorted((a, b) -> Double.compare(b.getScore(), a.getScore())) .collect(Collectors.toList()); // 5. 分页返回 int start = Math.min((page - 1) * pageSize, scored.size()); int end = Math.min(page * pageSize, scored.size()); return scored.subList(start, end).stream() .map(ps -> convertToVO(ps.getProduct())) .collect(Collectors.toList()); } }buildProductScore方法内部会计算四个子分数,并乘以各自权重后累加。这套代码我实际跑下来,一万条产品数据的推荐请求响应时间在200毫秒以内,性能完全够用。
3. Spring Boot后端核心接口实现
3.1 项目初始化与目录结构
Spring Boot版本我用的2.7.x,对应JDK 1.8。为什么不用Spring Boot 3.x?因为3.x强制要求JDK 17,而且很多老牌中间件客户端的兼容性在3.x早期版本里并不完善。做企业项目和毕设,稳妥永远比追新重要。
标准的项目目录结构如下:
com.example.bank ├── controller │ ├── ProductController.java │ ├── UserController.java │ └── RecommendController.java ├── service │ ├── ProductService.java │ ├── UserService.java │ └── RecommendService.java ├── mapper │ ├── ProductMapper.java │ ├── UserMapper.java │ └── RiskAssessmentMapper.java ├── entity │ ├── Product.java │ ├── User.java │ └── RiskAssessmentRecord.java ├── common │ ├── Result.java │ └── BusinessException.java └── config └── CorsConfig.javaResult.java是统一返回体,包含code、message、data三个字段。code为200表示成功,其他为失败。这个类虽然简单,但能保证全项目接口返回格式一致,前端处理起来非常舒服。
3.2 风险测评接口的设计细节
风险测评接口有两个:一个获取题目列表,一个提交答案。
@GetMapping("/assessment/questions") public Result<List<QuestionVO>> getQuestions() { List<Question> questions = assessmentService.getAllQuestions(); return Result.success(questions); } @PostMapping("/assessment/submit") public Result<RiskResultVO> submit(@RequestBody AssessmentSubmitDTO dto) { RiskResultVO result = assessmentService.evaluate(dto); return Result.success(result); }AssessmentSubmitDTO里包括userId和answerList,answerList是题目id和选项id的键值对列表。evaluate方法里根据用户的答案计算总分,然后映射成风险等级R1到R5。
风险等级映射规则可以参考这个表:
| 总分区间 | 风险等级 | 产品适配 |
|---|---|---|
| 0-20分 | R1 | 存款、国债、保本理财 |
| 21-40分 | R2 | 稳健型理财产品 |
| 41-60分 | R3 | 平衡型,可配置部分权益类 |
| 61-80分 | R4 | 可投混合型、指数型 |
| 81-100分 | R5 | 可投激进型产品 |
题目的设计要覆盖投资经验、投资期限偏好、亏损承受能力、收入稳定性这五个维度,每个维度两道题,一共十道题,每道题10分,选“保守”得低分,选“激进”得高分。
3.3 跨域配置:一个必踩的坑
Vue开发服务器默认运行在8080端口,Spring Boot运行在8081或者80端口,两者域名和端口不同,必然产生跨域问题。我见过太多人前后端联调时卡在这一步。
解决方式是在Spring Boot端配置全局跨域:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }注意allowedOriginPatterns和allowedOrigins的区别。如果allowCredentials(true),那么allowedOrigins("*")在较新版本的Spring Boot中会被拒绝,必须用allowedOriginPatterns("*")。这个细节坑了我整整一个下午,分享出来给各位避雷。
3.4 过滤器链中登录验证的实现
对于需要登录才能访问的接口(比如提交测评、推荐列表、收藏产品),我用了一个最简单的拦截器方案:
@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token == null || !TokenStorage.isValid(token)) { response.setStatus(401); return false; } return true; } }注册拦截器时排除登录注册接口:
registry.addInterceptor(loginInterceptor) .addPathPatterns("/**") .excludePathPatterns("/api/user/login", "/api/user/register", "/api/product/list");这个方案虽然简陋,没有用JWT或者Spring Security,但应付毕设和小型内部系统绰绰有余。真上生产环境的话,建议直接引入Sa-Token或者Spring Security,把token签发和校验做得更标准。
4. Vue前端实现:产品展示与交互流畅度
4.1 Vue项目的搭建
前端我用的是Vue 2 + Element UI的组合。为什么不用Vue 3?因为Element UI对Vue 2的支持最成熟,各种踩坑案例网上都能搜到,出了问题容易解决。Vue 3上Element Plus虽然也不错,但组件API变化比较大,新手学习成本偏高。
创建项目用Vue CLI:
npm install -g @vue/cli vue create bank-recommend-frontend创建时选择Manually select features,勾选Router、Vuex、Babel即可。ESLint建议选标准配置,虽然写代码时校验严格一点,但能帮你避免很多低级错误。
项目装依赖的环节,我在国内环境实测下来,直接用npm官方源会慢到怀疑人生。解决方案是切换淘宝镜像源:
npm config set registry https://registry.npmmirror.com这个步骤一定要在npm install之前做,否则装一半切源,依赖关系容易乱。
4.2 页面路由设计
前端页面划分成五个主要路由:
| 路径 | 组件 | 说明 |
|---|---|---|
| / | Home.vue | 首页,展示推荐产品列表 |
| /products | ProductList.vue | 产品列表,支持筛选和分页 |
| /product/:id | ProductDetail.vue | 产品详情页 |
| /assessment | Assessment.vue | 风险测评问卷页 |
| /favorites | Favorites.vue | 我的收藏 |
路由配置里做了一步懒加载处理:
const ProductDetail = () => import('../views/ProductDetail.vue')这样首屏只加载必要的组件代码,进入详情页时才去加载详情页对应的JS文件,首屏加载速度能快不少。这个优化在项目后期体现特别明显——产品详情页做得再重,也不影响首页打开速度。
4.3 产品推荐列表的卡片式展示
推荐列表页是用户打开系统后看到的第一个页面,它的设计直接决定了用户对系统的第一印象。我采用的是卡片式布局,每张卡片展示产品名称、银行名称、预期收益率、风险等级、起购金额、产品期限和“立即查看”按钮。
这里的风险等级展示做了一个色阶映射:
const riskColorMap = { 'R1': '#67C23A', 'R2': '#409EFF', 'R3': '#E6A23C', 'R4': '#F56C6C', 'R5': '#B5433A' }绿色到红色的渐变,用户即使不看文字,扫一眼颜色就能大致感知风险程度。这个细节在产品经理评审时被特别表扬过,花不了几行代码,但交互体验的直观性提升非常明显。
核心代码如下:
<template> <div class="product-grid"> <el-card v-for="item in productList" :key="item.id" class="product-card"> <div class="product-name">{{ item.productName }}</div> <div class="bank-name">{{ item.bankName }}</div> <el-tag :color="riskColorMap[item.riskLevel]" size="small"> {{ item.riskLevel }} </el-tag> <div class="expected-return">{{ item.expectedReturn }}%</div> <div class="meta">起购金额:{{ item.minPurchaseAmount }}元</div> <div class="meta">期限:{{ item.termDays }}天</div> <el-button type="primary" @click="goDetail(item.id)">立即查看</el-button> </el-card> </div> </template>后端接口返回字段与前端展示字段一一对应,前端不需要做任何额外数据处理,直接绑定渲染。这就是后端统一返回结构的好处——前端拿到数据就是干干净的,不用到处catch各种异常结构。
4.4 风险测评问卷的多步骤组件
风险测评页面我做成了一步一题的模式,用户点击“下一页”进入下一题,底部有进度条提示完成百分比。这种设计比十个题一次性展开更友好——用户不需要滚动页面,专注当前这一题,答题体验更像真正的银行柜台那个PAD上做问卷的感觉。
问卷数据的状态管理放在Vuex里:
const assessmentModule = { state: { currentStep: 0, answers: [], questions: [] }, mutations: { SET_STEP(state, step) { state.currentStep = step }, SET_ANSWER(state, { questionId, optionValue }) { const index = state.answers.findIndex(a => a.questionId === questionId) if (index > -1) { state.answers[index].optionValue = optionValue } else { state.answers.push({ questionId, optionValue }) } } } }用户点击“提交测评”后,前端将answers数组通过POST请求发给后端,后端计算得分和风险等级后返回,前端收到结果后跳转到推荐列表页,同时更新Vuex中存储的用户风险等级。这个流程天然流畅,用户从测评到看到推荐产品,整个路径不超过30秒。
4.5 用户行为埋点与偏好修正
只做“风险测评匹配”太基础了,我额外加了一个轻量级的用户行为记录模块。
用户查看产品详情、收藏产品、取消收藏、停留时长超过30秒等行为,都会异步POST到后端的/api/behavior接口。后端把这些行为记录在user_behavior_log表里,每天凌晨会有个定时任务做统计,给每个用户生成一个“行为偏好标签”,比如“偏好长期限”“偏好高收益”“关注国有大行”等。
这些标签目前主要用在后端推荐策略的微调上。比如用户在行为上明显表现出“只看了高风险产品”的背景下,即使风险测评是R2,系统也会在下次推荐列表中适当降低R1产品的排序,把用户看过的同类产品优先展示。
当然这只是辅助功能,核心的风险等级硬性过滤不会被覆盖。合规底线不能破,这个原则要时刻记住。
5. 完整请求链路演示:从测评到推荐的全过程
5.1 第一阶段:用户测评与数据提交
假设新用户张三注册登录后,第一次打开系统就被引导到风险测评页面。
前端Assessment.vue在mounted钩子里发请求拉取题目:
async mounted() { this.loading = true try { const { data } = await axios.get('/api/assessment/questions') this.questions = data.data this.loading = false } catch (e) { this.$message.error('获取测评题目失败') } }张三完成十道题,点击提交。前端把答案组装成后端要求的格式:
{ "userId": 1001, "answerList": [ { "questionId": 1, "optionValue": "A" }, { "questionId": 2, "optionValue": "C" }, { "questionId": 3, "optionValue": "B" } ] }后端evaluate方法逐题判分,10道题合计得到75分,映射为R4风险等级。测评记录持久化到risk_assessment_record表,同时更新user表的risk_level字段为R4。
5.2 第二阶段:推荐列表生成
张三测评完成,页面自动跳转到首页/,触发getRecommendList方法:
async getRecommendList() { const { data } = await axios.get('/api/recommend', { params: { userId: 1001, page: 1, pageSize: 10 } }) this.productList = data.data.list }后端收到请求后:
- 从库中查出张三的风险等级R4。
- 从
product表查出所有状态为在售的产品。 - 过滤掉风险等级高于R4的产品。
- 对其余产品计算加权得分。
- 按得分降序排列。
- 分页后返回给前端。
张三在前端看到的产品列表顺序,就是系统“认为”最适合张三的产品顺序。
5.3 第三阶段:收藏与行为跟踪
张三看中了一款某股份制银行的R3等级产品“年年盈1号”,点击收藏。前端调POST /api/favorite,后端保存收藏记录。
与此同时,前端默默向/api/behavior发送了一条浏览日志。整个过程用户无感知,但系统端已经积累了张三的行为数据,为后续推荐优化做储备。
这个完整链路看起来简单,但每一步之间都有数据流转和校验。评测结果影响推荐候选集,推荐候选集影响用户点击行为,用户点击行为反过来影响后续推荐排序。这个闭环是整个系统的核心价值所在。
6. 常见问题与性能优化实战
6.1 面试中经常被问到的问题
近期Spring Boot面试题里出现了很多与这类理财产品推荐系统相关的问题,各位要注意几个高频考点:
- Spring Boot自动装配的原理:为什么引入一个starter dependencies就能用?
@EnableAutoConfiguration做了什么?怎么复用自定义starter? - @ConfigurationProperties和@Value的区别:批量配置绑定怎么用?
@Validated怎么校验配置值? - RestControllerAdvice全局异常处理:业务异常和系统异常怎么区分?
- Spring Boot项目怎么处理跨域:(上面已经讲过了)
- Spring Boot自定义starter的实现:
spring.factories和AutoConfiguration.imports的区别。
我强烈建议做这类项目的同学,把自动装配原理和starter机制吃透。因为这种“推荐系统”类的项目,面试官特别喜欢让你解释spring-boot-starter-web到底做了什么,能不能自己写一个starter。能答上来说明你不是只会调注解,而是真的理解框架的设计思想。
6.2 前端打包后布局异常
很多人在本地开发时页面一切正常,但执行npm run build后部署到Nginx,发现页面布局全乱了,白屏或者CSS失效。
这个问题的原因90%是静态资源路径配置不对。解决方式:
在项目根目录创建vue.config.js:
module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/bank/' : '/' }然后在Nginx配置里匹配/bank/前缀指向前端打包好的dist目录:
location /bank/ { alias /opt/frontend/dist/; try_files $uri $uri/ /bank/index.html; }这里特别强调try_files配置——Vue Router使用了history模式时,刷新子路由页面会404,必须配这一行回退到index.html。
6.3 后端N+1查询问题
推荐接口刚写好时,性能很差。分析SQL日志发现,每查询一批产品就循环查一次银行表,N+1问题没跑了。
修复方式:
// 一次查出所有银行信息 Map<Long, BankInfo> bankMap = bankMapper.selectBatchIds(productIds) .stream().collect(Collectors.toMap(BankInfo::getId, Function.identity())); // 内存中关联组装 for (Product p : products) { BankInfo bank = bankMap.get(p.getBankId()); p.setBankName(bank.getName()); }1000条产品数据的查询耗时,从原来的3秒降到180毫秒。做企业应用,数据库查询次数越少越好,这是铁律。
6.4 Vue依赖安装的版本冲突
用npm install时如果提示ERR! ERESOLVE unable to resolve dependency tree,多半是依赖版本冲突。最快的解决方式:
npm install --legacy-peer-deps这个参数会让npm忽略peer dependency的自动校验,直接按照package.json里声明的版本安装。对Vue 2项目来说,这是我在多个开发机上实测下来最稳的方案。
7. 部署与上线运行
7.1 后端打包与启动
后端打jar包:
mvn clean package -DskipTests生产环境启动:
nohup java -jar bank-recommend.jar \ --server.port=8080 \ --spring.profiles.active=prod \ > app.log 2>&1 &--spring.profiles.active=prod指定生产环境配置,数据库连接、Redis连接等参数全部放在application-prod.yml里,和开发环境隔离。这是项目上线的基本素养——永远不要把你的本地数据库密码提交到Git仓库。
7.2 Nginx反向代理配置
生产环境前面罩一层Nginx,统一入口、处理HTTPS、做静态资源缓存:
server { listen 80; server_name your-domain.com; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { root /opt/frontend/dist; index index.html; try_files $uri $uri/ /index.html; } }这样前端页面和后端接口共用同一个域名,彻底规避跨域问题,也让用户访问路径更简洁。
7.3 部署环节最容易出问题的3个细节
第一个是后端接口报401。检查一下Nginx的proxy_set_header是否把Authorization头正确传给后端了。
第二个是前端刷新页面404。如果没配try_files,直接刷新/products页面就会404。这就是Nginx配置第7行的价值所在。
第三个是MySQL时区问题。连接串里必须带上serverTimezone=Asia/Shanghai,否则时间字段默认用服务器本地时区。我们踩过这个坑:用户晚上8点买的理财产品,数据库落库时间变成了中午12点,整整差了8个小时,排查了半天才发现是时区配置问题。
8. 可扩展方向:从“能用”到“好用”
这个系统做完一个完整版本后,有几个方向可以继续深化。
第一个方向是引入缓存层。产品列表和推荐结果是读多写少的场景,用Redis缓存热点产品数据和推荐结果,能显著降低数据库压力。产品数据变更时通过@CacheEvict注解失效缓存,实现起来也很简单。
第二个方向是对接消息队列。比如使用Spring Boot整合ActiveMQ或者RocketMQ,在产品开售前发送提醒短信给收藏过该产品的用户。这个功能做出来,产品的转化率会有很明显提升。
第三个方向是引入更精细的推荐策略。比如基于用户的历史购买记录做相似用户推荐(协同过滤思路),或者把银行偏好、地区偏好做进去。这些策略可以作为之前提到策略模式的扩展实现类,加进去不影响已有代码结构。
第四个方向是管理后台的完善。目前的管理功能只做了产品CRUD,可以做运营数据看板,展示每个产品的浏览量、收藏量、转化率,以及用户风险等级的分布情况。这些数据对银行的产品设计团队来说非常有价值。
做这类系统,最忌讳的就是一上来就追求大而全。先把用户、产品、推荐这三条主线打通,流程完整跑顺,再考虑各种花哨功能。我给很多做毕设的同学只讲一个原则:先垂直跑通,再水平扩展。主线流程能用,系统的价值就已经体现出来了,剩下来都是锦上添花。
最后再分享一个我实际开发中的体会:这种系统最关键的往往不是技术,而是对金融业务规则的理解。风险等级怎么映射、产品上架下架的时机、用户风险测评的合规要求,这些业务细节决定了系统能不能真正落地使用。技术层面,Spring Boot和Vue都是非常成熟的框架,网上资料一抓一大把,真正拉开差距的是对业务场景的把握。把业务规则搞清楚了,代码反而是水到渠成的事。