淘车湾项目实战十_项目总结_Bug调试及项目优化

O泡李华 7

淘车湾项目实战(十):项目总结、Bug 调试及项目优化

本章位置:第三阶段 Java 企业项目 + AI 助手
对应课程:淘车湾项目实战——项目总结及 Bug 调试及项目优化
前置知识:淘车湾项目实战(一)~(九)、SpringBoot、MyBatis、Redis、若依、Vue3、Activiti7、事务、AOP、DataScope
后续课程:企业开发流程
学习目标:把淘车湾项目从“功能能跑”提升到“结构清楚、数据一致、权限安全、便于排错、可以答辩、可以写进简历”的完整企业项目。


一、本章不是继续堆功能

这一章不再重点新增:

预约

工单

结算

审批

而是做:

全项目总结

全项目 Bug 排查

全项目优化

你现在要从:

“我把功能写出来了”

提升到:

“我知道整个系统为什么这样设计,
哪里容易出错,
怎么排查,
怎么优化,
怎么讲给面试官和老师听。”

二、整个淘车湾项目到底做了什么

一句话:

淘车湾是一个汽车养修业务管理系统,
覆盖预约、工单、服务项目与套餐、结算、支付和审批流程。

三、完整业务链

客户预约
↓
生成预约单
↓
转工单
↓
服务顾问录入服务项目/套餐
↓
技师施工
↓
工单完成
↓
生成结算单
↓
生成结算明细
↓
判断是否需要审批
↓
Activiti 工作流审批
↓
审批通过
↓
支付
↓
结算完成

四、系统主要角色

可以设计:

管理员

服务顾问

门店经理

财务

技师

五、管理员

主要负责:

用户管理

角色管理

菜单权限

数据字典

流程定义

基础数据维护

六、服务顾问

主要负责:

预约

客户

车辆

工单

结算创建

结算提交

七、技师

主要负责:

查看分配工单

施工

工单项目确认

完成施工

八、门店经理

主要负责:

优惠审批

九、财务

主要负责:

结算审核

支付

财务查询

十、系统架构总览

Vue3
+
Element Plus
↓
Axios
↓
SpringBoot Controller
↓
Service
↓
Mapper
↓
MyBatis
↓
MySQL

辅助能力:

Redis
Activiti
Spring Security
RuoYi RBAC
DataScope

十一、前后端职责边界

前端负责:

页面

交互

表单

基础校验

展示

按钮权限

后端负责:

身份

权限

业务规则

金额计算

状态校验

事务

数据库一致性

真正的数据权限

十二、最重要原则

前端不可信

不是说:

前端一定恶意

而是:

HTTP 请求可以被人为修改

所以:

所有真正业务规则
必须在后端再验证

十三、核心数据库模块

整个项目主要业务表可以分成:

客户车辆

预约工单

服务项目套餐

结算支付

审批流程

十四、客户车辆

例如:

car_customer

car_vehicle

关系:

Customer
1
:
N
Vehicle

十五、预约

car_appointment

主要字段:

appointment_id

appointment_no

customer_id

vehicle_id

dept_id

appointment_time

status

十六、工单

car_work_order

主要字段:

work_order_id

work_order_no

appointment_id

customer_id

vehicle_id

dept_id

technician_user_id

status

十七、工单明细

car_work_order_item

表示:

这次施工真正做了什么

十八、服务项目

car_service_item

属于:

服务主数据

例如:

更换机油

轮胎换位

空调清洗

十九、服务套餐

car_service_package

car_service_package_item

套餐与项目:

多对多

二十、结算主表

car_settlement

主要负责:

总额

优惠

应收

已付

支付状态

审批状态

二十一、结算明细

car_settlement_item

保存:

最终财务快照

二十二、审批记录

car_approval_record

保存:

提交

重新编辑

审批通过

审批拒绝

等业务动作。


二十三、Activiti 表

ACT_RE_*

ACT_RU_*

ACT_HI_*

ACT_GE_*

不要:

业务代码直接修改这些表

二十四、整套数据关系

Customer
↓
Vehicle
↓
Appointment
↓
WorkOrder
↓
WorkOrderItem
↓
Settlement
↓
SettlementItem
↓
Approval Process
↓
Payment

二十五、最重要的数据设计思想:快照

这个项目里至少有:

主数据

业务快照

财务快照

二十六、主数据

例如:

ServiceItem

当前标准价格:

300

以后可能改成:

350

二十七、业务快照

工单发生时:

WorkOrderItem.unitPrice = 300

它表示:

当时施工确认的价格

二十八、财务快照

结算时:

SettlementItem.originalAmount = 300
discount = 50
final = 250

二十九、正确关系

ServiceItem
当前主数据
↓
WorkOrderItem
施工快照
↓
SettlementItem
财务快照

三十、为什么快照重要

否则:

服务项目改价

会导致:

历史工单变价

历史结算变价

这在企业系统:

不可接受

三十一、状态设计总览

整个项目有很多状态:

预约状态

工单状态

结算状态

支付状态

审批状态

三十二、千万不要把所有状态塞一个字段

正确:

不同生命周期
不同字段

三十三、预约状态

例如:

待确认

已确认

已到店

已完成

已取消

三十四、工单状态

例如:

待派工

待施工

施工中

已完工

待结算

已结算

已取消

三十五、结算状态

DRAFT

SUBMITTED

COMPLETED

VOID

三十六、支付状态

UNPAID

PART_PAID

PAID

三十七、审批状态

NOT_REQUIRED

PENDING

PROCESSING

APPROVED

REJECTED

三十八、为什么状态多不是坏事

真正坏的是:

状态语义混乱

只要:

每个字段职责单一

状态多:

反而更清楚

三十九、状态更新的关键原则

不要:

UPDATE car_settlement
SET status = '1'
WHERE settlement_id = ?;

更安全:

UPDATE car_settlement
SET status = '1'
WHERE
    settlement_id = ?
    AND status = '0';

四十、为什么

这是:

带旧状态条件更新

可以防止:

重复提交

并发状态覆盖

四十一、金额模块总览

整个结算最核心:

totalAmount

discountAmount

receivableAmount

paidAmount

四十二、公式

totalAmount
=
SUM(SettlementItem.originalAmount)

四十三、优惠

discountAmount
=
SUM(SettlementItem.discountAmount)

四十四、应收

receivableAmount
=
SUM(SettlementItem.finalAmount)

且:

receivable
=
total
-
discount

四十五、支付

paidAmount
<=
receivableAmount

四十六、金额类型

Java:

BigDecimal

MySQL:

DECIMAL

四十七、不能用 double 做财务金额

例如:

0.1 + 0.2

浮点:

可能不是精确 0.3

四十八、BigDecimal 比较

不能:

a == b

也不建议用:

a.equals(b)

做金额业务比较。

推荐:

a.compareTo(b)

四十九、为什么 equals 有坑

例如:

1.0

1.00

数值相同,

但 scale:

不同

equals:

可能 false

五十、compareTo

new BigDecimal("1.0")
    .compareTo(
        new BigDecimal("1.00")
    )

结果:

0

五十一、事务总览

项目中最需要事务的方法:

预约转工单

套餐创建/修改

创建结算主表 + 明细

更新结算优惠

提交结算并启动流程

审批 Task + 业务状态

支付

五十二、事务注解

@Transactional(
    rollbackFor = Exception.class
)

五十三、为什么 rollbackFor

Spring 默认:

运行时异常回滚

企业项目:

显式 rollbackFor = Exception.class

更直观。


五十四、事务失效重点

常见:

this.xxx()

同类自调用。


五十五、错误

public void a() {

    this.b();
}

@Transactional
public void b() {
}

事务可能:

不生效

五十六、为什么

Spring 事务通常通过:

代理

自调用:

没有经过代理

五十七、还有哪些事务失效

方法不是 public

异常被 catch 后不抛

对象不是 Spring Bean

多数据源事务管理器不一致

异步线程

错误传播配置

五十八、异常被吞

错误:

try {
    mapper.insert(...);
} catch (
    Exception e
) {
    log.error(...);
}

然后:

方法正常结束

Spring 可能:

认为不需要回滚

五十九、正确

记录日志后重新抛

六十、全项目事务检查清单

1. @Transactional 是否在 Service

2. 方法是否 public

3. 是否通过 Spring Bean 调用

4. 是否同类 this 调用

5. 是否 catch 后吞异常

6. 是否多数据源

7. Activiti 是否与业务使用同事务管理器

8. 是否人为 new Service

9. 是否异步线程

六十一、权限系统总览

若依主要分:

认证

功能权限

数据权限

六十二、认证

回答:

你是谁

六十三、功能权限

@PreAuthorize

回答:

你能不能做这个动作

六十四、数据权限

@DataScope

回答:

你能操作哪些数据

六十五、例如

财务 A:

有 pay 权限

但是:

只能操作沈阳门店

六十六、完整权限链

登录认证
↓
@PreAuthorize
↓
DataScope / Ownership
↓
业务状态检查
↓
数据库操作

六十七、流程任务还有第四层

Task Assignee / CandidateGroup

所以审批:

@PreAuthorize
↓
Task Identity
↓
DataScope
↓
Business Status

六十八、前端 v-hasPermi

只是:

按钮显示控制

不是:

真正安全

六十九、为什么

用户可以:

直接调 HTTP

七十、所以后端必须

@PreAuthorize

七十一、DataScope Bug 最常见位置

很多人只在:

列表

加 DataScope。

详情:

selectById

没限制。

这就是:

水平越权

七十二、写操作更要防

例如:

A 店用户
PUT
B 店结算 ID

七十三、所有敏感写操作

应该:

先 requireAccessibleXXX()

七十四、Ownership 是什么

例如:

我的申请

不是部门范围,

而是:

applicant_user_id
=
currentUserId

七十五、Ownership 和 DataScope 区别

Ownership:

这是我自己的数据

DataScope:

这是我角色范围内的数据

七十六、流程图授权

可以:

Applicant Ownership
OR
Business DataScope

七十七、但不能

任意登录用户可看

七十八、Redis 在项目里该做什么

可以用于:

编号流水

缓存

验证码

会话/Token

热点数据

七十九、Redis 不应该做什么

不要:

把所有业务一致性都交给 Redis Lock

八十、能用数据库解决的

优先:

UNIQUE

状态条件 UPDATE

事务

乐观控制

八十一、为什么

数据库:

本身就是最终数据源

八十二、编号生成

例如:

seq:settlement:20260910

Redis:

INCR

八十三、但数据库仍要

UNIQUE(settlement_no)

八十四、为什么

Redis 不是:

最终唯一性约束

八十五、幂等与重复提交

项目里常见:

重复生成工单

重复生成结算

重复审批

重复支付

八十六、@RepeatSubmit

可以:

减少快速重复点击

八十七、但不能单独依赖

真正保证还需要:

状态

UNIQUE

条件 UPDATE

Task 状态

八十八、三层思想

例如结算:

前端禁用
+
@RepeatSubmit
+
UNIQUE(work_order_id)

八十九、并发重点 1:重复结算

两个请求:

都 count = 0

所以:

count 检查不能作为最终保证

九十、最终:

UNIQUE(work_order_id)

九十一、并发重点 2:支付

两个请求:

都看到 paidAmount = 100

然后:

A + 500
B + 500

如果直接 update:

可能覆盖

九十二、推荐

UPDATE car_settlement
SET
    paid_amount = #{newPaid}
WHERE
    settlement_id = #{id}
    AND paid_amount = #{oldPaid};

九十三、更新 0 行

说明:

并发数据已经变化

九十四、并发重点 3:Task claim

使用:

TaskService.claim

不要:

自己 UPDATE ACT_RU_TASK

九十五、并发重点 4:重复审批

Task complete 后:

TaskService 查不到运行任务

第二次:

自动失败

同时:

ApprovalRecord.task_id UNIQUE

再兜底。


九十六、SQL 性能总览

项目常见性能问题:

全表扫描

模糊搜索

N+1

重复查询

无索引

索引顺序错误

SELECT *

九十七、索引不是越多越好

索引会增加:

存储

INSERT 成本

UPDATE 成本

DELETE 成本

九十八、索引应该服务真实查询

例如:

我的申请

查询:

applicant_user_id
+
submit_time

所以:

(applicant_user_id, submit_time)

很合理。


九十九、结算门店查询

dept_id

status

经常一起过滤。

可以:

(dept_id, status)

一百、最左前缀

组合索引:

(a,b,c)

可用于:

a

a,b

a,b,c

不一定有效利用:

b,c

一百零一、索引选择性

如果字段:

只有 0/1

选择性很低。

单独:

status

未必有很好效果。


一百零二、dept + status

相比单 status:

更有实际价值

一百零三、主键选择

推荐:

BIGINT 自增 / 分布式 ID

当前单体:

自增 BIGINT

简单稳定。


一百零四、业务编号

例如:

settlement_no

不等于:

数据库主键

一百零五、为什么

业务编号:

给人看

用于对账

主键:

数据库关联

一百零六、N+1 是什么

比如查询:

20 个套餐

然后循环:

每个套餐查一次项目

总查询:

1 + 20

就是:

N+1

一百零七、优化

一次查:

所有 package_id IN (...)

然后:

Java 分组

一百零八、另一个 N+1

待办任务每条:

查一次 Settlement

任务量很大时:

也会成为问题

一百零九、课程项目

任务量小:

可以接受

一百一十、企业优化

先收集:

businessIds

批量查询:

Settlement IN (...)

再:

Map 组装

一百一十一、先不要盲目优化

原则:

先发现瓶颈
再优化

一百一十二、SQL 日志

开发环境:

打开 MyBatis SQL 日志

关注:

SQL 数量

参数

执行时间

一百一十三、不要只看“页面慢”

要判断:

前端慢

网络慢

后端慢

SQL 慢

流程引擎慢

一百一十四、全链路排查

Browser Network
↓
Controller
↓
Service
↓
Mapper
↓
SQL
↓
Database

一百一十五、Bug 调试的正确顺序

不要:

一报错就乱改代码

一百一十六、第一步:复现

必须知道:

怎么操作一定会出错

一百一十七、第二步:缩小范围

判断:

前端

Controller

Service

Mapper

数据库

Activiti

哪一层。


一百一十八、第三步:看真实请求

Chrome:

F12
→ Network

看:

URL

Method

Request Payload

Response

一百一十九、第四步:后端日志

看:

异常类型

第一处业务栈

SQL

参数

一百二十、第五步:IDEA 断点

重点:

输入值

状态值

Mapper rows

流程 task

金额

一百二十一、第六步:数据库验证

用 SQL:

直接检查真实数据

一百二十二、第七步:修复最小问题

不要一次:

重写一大片

一百二十三、第八步:回归测试

不仅:

刚才报错功能

还要测试:

相关功能

一百二十四、Bug 分类

建议分成:

前端

参数

业务

事务

权限

数据库

流程

并发

性能

一百二十五、前端 Bug 1:字段名不一致

后端:

paymentStatus

前端:

payStatus

页面:

空

一百二十六、排查

Network Response:

先看真实 JSON

一百二十七、前端 Bug 2:按钮不显示

检查:

v-if

v-hasPermi

状态

权限字符串

一百二十八、前端 Bug 3:按钮显示但 403

说明:

前端权限缓存
和
后端真实权限
不一致

检查:

sys_menu.perms

角色授权

/getInfo permissions

一百二十九、前端 Bug 4:金额显示 NaN

检查:

null

undefined

Number()

字段名

一百三十、前端 Bug 5:流程图空白

检查:

容器高度

BPMN DI

importXML

Drawer opened

一百三十一、前端 Bug 6:流程图 Marker 不生效

检查:

activityId

processDefinition 版本

ElementRegistry

:deep CSS

一百三十二、参数 Bug 1:400

通常:

JSON 格式

@NotNull

@Valid

日期格式

一百三十三、参数 Bug 2:MyBatis 参数找不到

例如:

Parameter 'settlementNo' not found

检查:

@Param

XML test

#{xxx}

一百三十四、业务 Bug 1:非法状态操作

例如:

施工中
直接生成结算

应该:

Service 拒绝

一百三十五、业务 Bug 2:审批中修改金额

必须:

DRAFT 才允许改

一百三十六、业务 Bug 3:审批拒绝还能支付

支付 Service:

必须检查 approvalStatus

一百三十七、业务 Bug 4:服务项目改价影响历史

说明:

历史详情 JOIN 当前价格

应该:

使用快照

一百三十八、事务 Bug 1:主表有,明细没有

检查:

@Transactional

是否真的生效。


一百三十九、事务 Bug 2:工单状态已更新,但结算失败

说明:

不在同一事务

一百四十、事务 Bug 3:流程启动了,业务没更新

检查:

Activiti 与业务事务

是否统一。


一百四十一、权限 Bug 1:A 门店看 B 门店

检查:

DataScope

一百四十二、权限 Bug 2:列表安全,详情越权

详情:

不能裸 selectById

一百四十三、权限 Bug 3:申请人看别人申请

检查:

applicant_user_id

是否强制当前用户。


一百四十四、权限 Bug 4:审批人自审

检查:

applicantUserId
==
currentUserId

一百四十五、流程 Bug 1:待办为空

检查:

ACT_RU_TASK

candidateGroup

roleKey

assignee

DataScope

一百四十六、流程 Bug 2:Task complete 后查不到

这是:

正常

用:

HistoryService

一百四十七、流程 Bug 3:旧流程图高亮错

检查:

是不是加载 latestVersion

正确:

实例自己的 processDefinitionId

一百四十八、流程 Bug 4:经理拒绝后财务仍高亮

说明:

连线推导错误

一百四十九、并发 Bug 1:重复结算

最终兜底:

UNIQUE(work_order_id)

一百五十、并发 Bug 2:重复审批记录

UNIQUE(task_id)

一百五十一、并发 Bug 3:支付覆盖

使用:

oldPaidAmount 条件更新

一百五十二、数据库 Bug 1:中文乱码

统一:

数据库 utf8mb4

连接 URL

IDEA UTF-8

项目 UTF-8

一百五十三、数据库 Bug 2:时间偏差

检查:

JVM 时区

MySQL 时区

前端时间

一百五十四、数据库 Bug 3:金额 scale 错

检查:

DECIMAL(12,2)

@Digits

BigDecimal

一百五十五、数据库 Bug 4:唯一索引冲突

先判断:

业务真的重复

还是:

唯一规则设计不合理

一百五十六、全项目数据库一致性 SQL

1. 重复结算

SELECT
    work_order_id,
    COUNT(*)
FROM car_settlement
GROUP BY work_order_id
HAVING COUNT(*) > 1;

一百五十七、2. 主表总金额不等于明细

SELECT
    s.settlement_id,
    s.total_amount,
    SUM(i.original_amount)
FROM car_settlement s
JOIN car_settlement_item i
  ON i.settlement_id =
     s.settlement_id
GROUP BY
    s.settlement_id,
    s.total_amount
HAVING
    s.total_amount
    <>
    SUM(i.original_amount);

一百五十八、3. 优惠不一致

SELECT
    s.settlement_id
FROM car_settlement s
JOIN car_settlement_item i
  ON i.settlement_id =
     s.settlement_id
GROUP BY
    s.settlement_id,
    s.discount_amount
HAVING
    s.discount_amount
    <>
    SUM(i.discount_amount);

一百五十九、4. 应收不一致

SELECT
    s.settlement_id
FROM car_settlement s
JOIN car_settlement_item i
  ON i.settlement_id =
     s.settlement_id
GROUP BY
    s.settlement_id,
    s.receivable_amount
HAVING
    s.receivable_amount
    <>
    SUM(i.final_amount);

一百六十、5. 明细公式错误

SELECT *
FROM car_settlement_item
WHERE
    final_amount
    <>
    original_amount
    -
    discount_amount;

一百六十一、6. 支付超额

SELECT *
FROM car_settlement
WHERE
    paid_amount
    >
    receivable_amount;

一百六十二、7. PROCESSING 但没有流程 ID

SELECT *
FROM car_settlement
WHERE
    approval_status = '2'
    AND process_instance_id IS NULL;

一百六十三、8. 已拒绝却有支付

SELECT *
FROM car_settlement
WHERE
    approval_status = '4'
    AND paid_amount > 0;

一百六十四、9. 提交申请没有申请人

SELECT *
FROM car_settlement
WHERE
    submit_time IS NOT NULL
    AND applicant_user_id IS NULL;

一百六十五、10. 重复审批 Task

SELECT
    task_id,
    COUNT(*)
FROM car_approval_record
WHERE task_id IS NOT NULL
GROUP BY task_id
HAVING COUNT(*) > 1;

一百六十六、代码分层检查

Controller:

不能写大量业务逻辑

一百六十七、Controller 只负责

接参数

校验

权限

调用 Service

返回结果

一百六十八、Service

负责:

业务规则

事务

状态

金额

跨表协调

一百六十九、Mapper

负责:

数据库访问

一百七十、DTO

负责:

接收请求

一百七十一、VO

负责:

返回页面

一百七十二、Entity

负责:

数据库持久化

一百七十三、为什么不要 Entity 直接全网传

Entity:

字段多

可能有内部字段

与表结构耦合

一百七十四、DTO/VO 的价值

接口稳定

字段安全

语义清晰

一百七十五、异常处理

不要:

Controller 到处 try/catch

一百七十六、业务异常

throw new ServiceException(...)

一百七十七、统一异常处理

由:

GlobalExceptionHandler

统一转:

AjaxResult

一百七十八、为什么

减少:

重复代码

一百七十九、日志设计

分成:

系统操作日志

应用日志

业务审批记录

一百八十、系统操作日志

若依:

@Log

记录:

谁做了什么接口动作

一百八十一、应用日志

例如:

log.info(
    "创建结算成功, settlementId={}",
    id
);

一百八十二、业务审批记录

car_approval_record

记录:

谁批准/拒绝了什么

一百八十三、不要日志写密码/Token

也不要写:

完整身份证

敏感支付信息

一百八十四、接口设计检查

尽量:

资源路径稳定

动作语义清楚

一百八十五、例如

POST /settlement/{id}/submit

比:

POST /updateSettlementStatus

更清晰。


一百八十六、业务动作接口

推荐:

submit

pay

claim

unclaim

approve

reopen

一百八十七、为什么不万能 update

因为:

每个动作都有独立规则

一百八十八、前端优化总览

不要只追求:

好看

先保证:

状态正确

按钮正确

错误提示正确

Loading 正确

一百八十九、列表 Loading

发请求:

loading = true

finally:

false

一百九十、为什么

避免:

用户不知道页面是否在请求

一百九十一、按钮 Loading

尤其:

提交

支付

审批

建议:

防止重复点

一百九十二、但后端仍要防重复

前端 loading:

只是 UX

一百九十三、表单校验

前端:

提高体验

后端:

最终校验

一百九十四、金额展示

统一:

两位小数

一百九十五、不要不同页面

300

300.0

300.00

混乱。


一百九十六、状态字典

使用:

useDict

统一。


一百九十七、不要多个页面手写

0 = 草稿
1 = 提交

一百九十八、组件复用

前面已经抽:

ApprovalDrawer

ApprovalTimeline

ProcessDiagram

继续复用。


一百九十九、不要复制粘贴审批页面三份

否则:

修 Bug 要改三处

二百、后端复用也是一样

例如:

SettlementDetailVO

应该:

管理员详情

审批详情

我的申请详情

共同复用。


二百零一、但复用不等于权限复用

同一个查询结果:

可以复用

授权:

必须按场景分别检查

二百零二、性能优化顺序

正确:

先测
↓
找慢点
↓
优化

二百零三、不要

先加 Redis

先加 MQ

先加分布式锁

先改微服务

二百零四、当前项目是单体

单体不是:

落后

对于课程项目:

更合适

二百零五、为什么

业务规模:

不需要微服务复杂度

二百零六、优化 1:避免 SELECT *

只查:

页面需要字段

二百零七、优化 2:避免循环查数据库

批量:

IN 查询

二百零八、优化 3:分页

列表:

必须分页

二百零九、优化 4:索引

根据:

WHERE + ORDER BY

设计。


二百一十、优化 5:状态字段

尽量:

短编码

展示:

字典

二百一十一、优化 6:主从表批量插入

例如:

SettlementItems

使用:

batch insert

二百一十二、优化 7:避免重复业务查询

一个 Service 方法:

已经查到 Settlement

不要后面:

又查三次

二百一十三、优化 8:Map

批量数据:

转 Map

用于:

O(1) 匹配

二百一十四、优化 9:只缓存真正适合的数据

例如:

服务字典

热点配置

二百一十五、不要缓存

频繁变化的审批当前节点

除非:

明确需要

二百一十六、优化 10:业务状态用枚举

不要:

if (
    "2".equals(status)
)

到处出现。


二百一十七、正确

ApprovalStatus
    .PROCESSING
    .getCode()

二百一十八、优化 11:魔法字符串集中

例如:

SETTLEMENT

APPROVE

REJECT

可以:

常量 / 枚举

二百一十九、优化 12:公共校验方法

例如:

requireDraft

requireAccessibleSettlement

validateSettlementConsistency

validateNoSelfApproval

二百二十、为什么

减少:

重复 if

二百二十一、但不要为了抽象而抽象

一个只调用一次的:

两行代码

不一定:

必须抽方法

二百二十二、代码可读性目标

一眼看出业务步骤

二百二十三、一个好的 Service 方法应该像业务流程

例如:

查工单
↓
校验
↓
算金额
↓
保存结算
↓
保存明细
↓
更新状态

二百二十四、不要变成

500 行 if

二百二十五、数据库约束检查

关键 UNIQUE:

settlement_no

work_order_id

(settlement_id, work_order_item_id)

approval_record.task_id

二百二十六、为什么数据库约束非常重要

应用代码:

可能有 Bug

数据库:

最后一道防线

二百二十七、但数据库约束不是替代业务校验

正确:

Service 友好校验

+
数据库最终兜底

二百二十八、全项目索引建议

举例:

car_appointment:
(dept_id, status)
appointment_no unique

二百二十九、工单:

work_order_no unique

(dept_id, status)

appointment_id

二百三十、结算:

settlement_no unique

work_order_id unique

(dept_id, status)

approval_status

(applicant_user_id, submit_time)

二百三十一、结算明细:

settlement_id

(settlement_id, work_order_item_id) unique

二百三十二、审批记录:

task_id unique

(business_type, business_id, create_time)

二百三十三、不要机械复制索引

最终:

根据真实 SQL EXPLAIN

确认。


二百三十四、EXPLAIN 要看什么

重点:

type

key

rows

Extra

二百三十五、type

一般:

const

ref

range

往往比:

ALL

更好。


二百三十六、ALL

通常表示:

全表扫描

但小表:

不一定就是问题

二百三十七、rows

表示优化器估计:

需要扫描多少行

二百三十八、Extra

关注:

Using filesort

Using temporary

但不能:

看到就恐慌

二百三十九、慢 SQL 排查步骤

1. 找具体 SQL

2. 带真实参数执行

3. EXPLAIN

4. 检查索引

5. 检查返回行数

6. 检查 JOIN

7. 检查 ORDER BY

二百四十、不要先猜


二百四十一、Activiti 优化

流程定义:

不要每次提交都重新部署

二百四十二、正确

管理员:

提前部署

业务:

只启动流程

二百四十三、为什么

部署:

会产生新版本

如果每次提交都 deploy:

版本爆炸

二百四十四、流程变量优化

只放:

轻量上下文

二百四十五、不要放整个 Java 对象


二百四十六、历史级别

需要流程图高亮:

要保证 HistoricActivity 可用

二百四十七、但历史越详细

数据库:

历史数据越多

生产:

要平衡

二百四十八、当前课程项目

优先:

保证历史和流程图正常

二百四十九、流程版本

一定:

历史实例加载自己的 Definition

二百五十、不要:

latestVersion

显示旧流程。


二百五十一、全项目安全检查

认证

是否所有业务接口都需要登录

二百五十二、功能权限

是否敏感接口都有 @PreAuthorize

二百五十三、数据权限

列表

详情

修改

支付

审批

导出

是否都考虑。


二百五十四、参数越权

是否存在:

前端传 userId

deptId

approvalStatus

paymentStatus

finalAmount

然后后端直接信任。


二百五十五、金额篡改

检查:

totalAmount

unitPrice

finalAmount

是否由后端重算。


二百五十六、流程越权

检查:

taskId

processInstanceId

businessId

是否验证。


二百五十七、自审

检查:

Applicant
!=
Approver

二百五十八、XSS

审批意见:

不要 v-html

二百五十九、SQL 注入

MyBatis:

优先 #{}

不要:

${用户输入}

二百六十、DataScope 特例

若依:

${params.dataScope}

属于:

框架内部生成 SQL 片段

和普通用户输入:

不是一回事

二百六十一、文件安全

如果后续有上传:

校验类型

大小

路径

当前:

不是项目核心

二百六十二、操作日志

敏感业务:

提交

审批

支付

作废

重开

应该:

有 @Log

二百六十三、Cursor 全项目审查提示词 1:架构

请审查当前淘车湾项目的代码分层。

重点:
1. Controller 是否写了过多业务逻辑
2. Service 是否承担事务与业务规则
3. Mapper 是否只负责数据访问
4. DTO/VO/Entity 是否混用
5. 是否存在重复业务代码
6. 是否存在循环依赖
7. 是否存在 this.xxx 导致事务失效
8. 输出最重要问题,不做无意义大重构

二百六十四、Cursor 全项目审查提示词 2:权限

请审查淘车湾项目全部敏感接口权限。

覆盖:
预约
工单
服务项目
套餐
结算
支付
流程定义
待办
已办
我的申请
流程图

检查:
1. @PreAuthorize
2. DataScope
3. Ownership
4. Task Assignee/CandidateGroup
5. 是否存在只限制列表不限制详情
6. 是否信任前端 userId/deptId/businessId
7. 是否存在自审
8. 输出明确文件和方法

二百六十五、Cursor 全项目审查提示词 3:金额

请审查淘车湾所有金额代码。

要求:
1. Java 是否全部 BigDecimal
2. MySQL 是否 DECIMAL
3. 是否存在 double/float 财务计算
4. totalAmount 是否由后端计算
5. Settlement 主表金额是否来自明细汇总
6. discount 是否可能大于 original
7. paidAmount 是否可能超过 receivable
8. 是否使用 compareTo
9. 是否存在前端可篡改 unitPrice/finalAmount
10. 输出风险最高的代码位置

二百六十六、Cursor 全项目审查提示词 4:事务

请审查淘车湾项目事务。

重点方法:
预约转工单
套餐新增修改
创建结算
修改结算
提交审批
流程审批
支付

检查:
1. @Transactional 是否生效
2. 是否 public
3. 是否 this 自调用
4. 是否 catch 吞异常
5. 是否 Spring Bean 调用
6. Activiti 和业务是否共享事务管理器
7. 是否有跨表部分成功风险
8. 输出可复现测试方案

二百六十七、Cursor 全项目审查提示词 5:并发

请审查淘车湾项目并发与重复提交。

检查:
1. 同预约重复生成工单
2. 同工单重复结算
3. 双击提交
4. 双击支付
5. 双人同时支付
6. 双人同时 claim
7. 重复审批
8. reopen 并发
9. UNIQUE 是否完整
10. 是否使用 oldStatus/oldPaidAmount 条件更新
11. 不要一上来就建议所有地方加 Redis 锁

二百六十八、Cursor 全项目审查提示词 6:SQL

请审查淘车湾 Mapper XML 和 SQL。

重点:
1. SELECT *
2. N+1
3. 循环查询
4. 缺少分页
5. JOIN 是否合理
6. WHERE 条件是否匹配索引
7. applicant_user_id + submit_time
8. dept_id + status
9. settlement_id 子表查询
10. 是否错误使用 ${用户输入}
11. 输出建议索引与对应 SQL

二百六十九、Cursor 全项目审查提示词 7:Activiti

请审查淘车湾 Activiti 集成。

检查:
1. 是否业务代码直接操作 ACT_* 表
2. 流程是否重复部署
3. businessKey 是否 settlementId
4. processInstanceId 是否回写业务表
5. Task complete 是否校验 assignee
6. candidateGroup 是否使用 roleKey
7. 是否禁止自审
8. 旧流程图是否使用自己的 processDefinitionId
9. 已办是否使用 HistoryService
10. 流程变量是否塞入整个业务对象
11. SpringBoot3 与当前 Activiti 依赖兼容性

二百七十、Cursor 全项目审查提示词 8:前端

请审查淘车湾 Vue3 前端。

检查:
1. API 字段名是否和后端一致
2. v-hasPermi 是否正确
3. 是否把前端权限当真正权限
4. 提交/支付/审批是否有 loading
5. 金额展示是否统一两位小数
6. 是否重复复制 ApprovalTimeline/ProcessDiagram
7. bpmn-js 是否 destroy
8. scoped CSS 是否需要 :deep
9. Drawer 是否在 opened 后 fit-viewport
10. 错误提示是否清晰

二百七十一、项目最终测试建议

不要只:

点一遍页面

要按:

角色
+
状态
+
异常
+
并发

测试。


二百七十二、角色测试

管理员:

能管理全部配置

二百七十三、服务顾问

能创建和提交
不能审批

二百七十四、经理

能经理审批
不能直接财务支付

二百七十五、财务

能财务审批和支付

二百七十六、门店隔离

沈阳:

不能看大连

二百七十七、异常状态测试

施工中不能结算

审批中不能改金额

拒绝不能支付

已支付不能 reopen

二百七十八、异常参数

优惠负数

优惠大于原价

支付负数

支付超额

空 taskId

非法 settlementId

二百七十九、并发测试

至少:

双击提交

双击审批

两个浏览器支付

两个审批人 claim

二百八十、事务测试

故意:

中途 throw RuntimeException

看:

是否全部回滚

二百八十一、项目答辩怎么讲

不要:

从代码第一页讲到最后

二百八十二、正确结构

1. 项目背景

2. 用户角色

3. 核心业务流程

4. 技术架构

5. 数据库设计

6. 难点

7. 解决方案

8. 演示

9. 项目总结

二百八十三、项目背景示例

可以讲:

传统汽车养修业务中,
预约、工单、服务项目、结算和审批信息容易分散,
因此本项目构建统一的汽车养修业务管理系统,
实现从预约到结算审批的完整流程数字化。

二百八十四、技术架构

SpringBoot
MyBatis
MySQL
Redis
Activiti
Vue3
Element Plus
RuoYi

二百八十五、为什么用若依

可以答:

若依提供成熟的用户、角色、菜单、权限、数据权限、
日志和基础后台能力,
可以把开发重点放在汽车养修核心业务上。

二百八十六、为什么用 Activiti

审批节点可能变化,
使用 BPMN 工作流比硬编码 if/else 更易维护,
并且能提供待办、已办、历史和流程图。

二百八十七、为什么用 Redis

用于高频轻量数据和编号流水等场景,
减少不必要数据库压力,
同时不把核心业务一致性依赖完全放在 Redis 上。

二百八十八、项目最大难点 1:金额一致性

回答:

结算金额不能信任前端,
后端从工单快照生成结算明细,
再从明细统一汇总主表总额、优惠和应收,
同时使用 BigDecimal + DECIMAL。

二百八十九、难点 2:历史价格

服务项目会改价,
因此通过 WorkOrderItem 和 SettlementItem
保存业务快照和财务快照,
避免历史数据随主数据变化。

二百九十、难点 3:权限

使用若依 @PreAuthorize 控制功能权限,
@DataScope 控制门店数据范围,
工作流审批再校验 Task Assignee/CandidateGroup,
我的申请使用 Applicant Ownership。

二百九十一、难点 4:工作流

结算提交后以 settlementId 作为 businessKey
启动 Activiti ProcessInstance,
将 processInstanceId 回写业务表,
再通过 TaskService/HistoryService 完成待办、已办和流程图。

二百九十二、难点 5:并发

通过唯一约束、旧状态条件更新、
oldPaidAmount 条件更新、Task claim/complete
以及 @RepeatSubmit 多层控制。

二百九十三、答辩演示顺序

推荐:

1. 服务项目

2. 套餐

3. 创建预约

4. 转工单

5. 添加服务

6. 完工

7. 生成结算

8. 填优惠

9. 提交审批

10. 经理登录审批

11. 财务登录审批

12. 查看流程图

13. 支付

14. 我的申请查看结果

二百九十四、为什么这样演示

它是一条:

完整业务链

老师:

最容易理解

二百九十五、演示前必须准备

测试账号

测试角色

测试菜单权限

测试门店

测试客户

测试车辆

测试项目

测试套餐

已部署 BPMN

二百九十六、不要现场临时造所有数据

提前:

准备好

二百九十七、答辩前数据库备份

非常推荐:

mysqldump

或:

Navicat 导出

二百九十八、为什么

现场演示改乱数据:

可以快速恢复

二百九十九、答辩前 Git Tag

例如:

git tag v1.0-defense

三百、为什么

确保:

答辩版本固定

三百零一、答辩前不要大改依赖

尤其:

SpringBoot

Activiti

Vue

bpmn-js

三百零二、为什么

越临近演示:

稳定优先于升级

三百零三、README 应该写什么

至少:

项目介绍

技术栈

功能模块

环境要求

数据库初始化

后端启动

前端启动

测试账号

流程部署说明

三百零四、不要 README 只有一句

这是淘车湾项目

三百零五、简历项目描述怎么写

不要:

负责写接口、写页面

太普通。


三百零六、推荐项目描述

基于 Spring Boot、Vue3、MyBatis、MySQL、Redis 与 Activiti
实现汽车养修业务管理系统,
覆盖预约、工单、服务项目/套餐、结算、支付及审批流程,
并基于若依完成 RBAC 与门店级数据权限控制。

三百零七、职责 1

负责预约到工单、工单到结算的核心业务链设计,
通过状态机与事务控制保证跨表业务一致性。

三百零八、职责 2

设计服务项目、套餐、工单明细和结算明细快照机制,
避免基础价格调整影响历史业务和财务数据。

三百零九、职责 3

集成 Activiti 工作流,
以业务 ID 关联流程实例,
实现审批待办、已办、审批记录及 BPMN 流程图高亮。

三百一十、职责 4

基于若依 RBAC、@PreAuthorize、@DataScope
实现功能权限、门店数据权限和流程任务权限控制。

三百一十一、职责 5

使用 BigDecimal + DECIMAL 处理结算金额,
通过唯一约束、条件更新和事务机制防止重复结算、重复审批和支付并发覆盖。

三百一十二、简历不要写太多

建议:

项目描述 2~3 行

职责 4~6 条

三百一十三、面试时如何讲这个项目

面试官问:

你项目里最难的地方是什么?

不要回答:

前端页面

三百一十四、可以答

我认为难点主要有三个:

第一是结算金额一致性;
第二是审批流程和业务状态一致性;
第三是多层权限与门店数据隔离。

三百一十五、然后分别展开

这样:

很像真实开发

三百一十六、面试问题:为什么不用一个 status

答:

工单、结算、支付、审批属于不同生命周期。
如果合并到一个状态字段,
组合状态会爆炸。
因此拆成多个独立状态字段。

三百一十七、面试问题:为什么要快照

答:

主数据会变,
历史业务不能变。

三百一十八、面试问题:为什么前端金额不可信

答:

HTTP 请求可以篡改,
金额必须以后端数据库和业务规则重算。

三百一十九、面试问题:为什么还要数据库唯一索引

答:

应用层检查无法完全避免并发,
数据库唯一约束作为最终一致性兜底。

三百二十、面试问题:为什么不用 Redis Lock 防所有并发

答:

很多并发问题本身可以通过
唯一约束、状态条件更新、事务和引擎原子操作解决。
分布式锁会增加复杂度,
不应该在单体业务里滥用。

三百二十一、面试问题:Activiti 和业务表怎么关联

答:

businessKey = settlementId

processInstanceId 回写 car_settlement

三百二十二、面试问题:为什么已办用 HistoryService

答:

Task 完成后不再属于 Runtime Task,
因此历史任务使用 HistoricTaskInstance 查询。

三百二十三、面试问题:为什么旧流程不能加载 latest BPMN

答:

历史实例绑定的是启动时的流程定义版本,
必须按自己的 processDefinitionId 加载,
否则节点 ID 可能错位。

三百二十四、面试问题:DataScope 和 @PreAuthorize 区别

答:

@PreAuthorize 控制能不能做,
DataScope 控制能做哪些数据。

三百二十五、面试问题:什么是水平越权

答:

用户有查看功能权限,
但通过修改业务 ID 查看了不属于自己的数据。

三百二十六、项目还能优化什么

如果老师问:

系统还有什么不足?

不要回答:

没有不足

三百二十七、可以答

当前系统以单体架构为主,
流程规则和支付记录也做了课程级简化。

如果业务规模进一步扩大,
可以进一步优化任务查询索引、
完善支付流水、引入消息通知,
并强化流程版本与审批实例管理。

三百二十八、注意

不要主动说:

项目问题很多

三百二十九、要表达

当前设计符合当前规模
未来有明确扩展方向

三百三十、项目最终目录建议

后端:

ruoyi-car
├─ controller
├─ service
├─ service.impl
├─ mapper
├─ domain
├─ dto
├─ vo
├─ enums
├─ workflow
└─ resources
   ├─ mapper
   └─ processes

三百三十一、前端:

src
├─ api
│  └─ car
└─ views
   └─ car
      ├─ appointment
      ├─ workOrder
      ├─ serviceItem
      ├─ servicePackage
      ├─ settlement
      └─ workflow
         ├─ definition
         ├─ application
         ├─ task
         └─ components

三百三十二、枚举建议

WorkOrderStatus

SettlementStatus

PaymentStatus

ApprovalStatus

ApprovalAction

ServiceSourceType

三百三十三、常量建议

WorkflowConstants

RedisKeyConstants

三百三十四、不要所有字符串散落


三百三十五、项目完成标准

不是:

页面能打开

而是:

核心业务闭环能完成

三百三十六、闭环检查 1

预约
→ 工单

三百三十七、闭环 2

工单
→ 服务项目
→ 完工

三百三十八、闭环 3

完工
→ 结算

三百三十九、闭环 4

结算
→ 审批

三百四十、闭环 5

审批
→ 支付

三百四十一、闭环 6

申请人
→ 查看结果

三百四十二、闭环 7

审批人
→ 待办
→ 已办

三百四十三、闭环 8

流程图
→ 当前/历史正确

三百四十四、Bug 修复优先级

P0:

数据错乱

越权

金额错误

支付错误

事务部分提交

三百四十五、P1

核心流程无法完成

审批卡死

状态不一致

三百四十六、P2

性能慢

分页错误

按钮状态错误

三百四十七、P3

UI 间距

文案

样式

三百四十八、为什么要这样排

不要:

金额错了还在调按钮颜色

三百四十九、最终调试顺序

安全
↓
数据一致
↓
核心流程
↓
性能
↓
体验
↓
样式

三百五十、Git 收尾流程

git status

三百五十一、检查修改

git diff

三百五十二、运行测试

后端启动

前端启动

完整链路测试

三百五十三、提交

git add .

三百五十四、Commit

git commit -m "chore: finalize taochewan project"

三百五十五、Tag

git tag v1.0

三百五十六、为什么不要直接 git add . 就提交

先:

git status

git diff

检查:

有没有密码

配置文件

临时文件

三百五十七、敏感配置

不要提交:

真实数据库密码

Redis 密码

Token

云服务 SecretKey

三百五十八、使用

环境变量

本地配置

application-prod.yml 外部配置

三百五十九、最终 Cursor 全项目修复提示词

请对当前淘车湾项目做最后一次发布前审查。

不要大规模重构,不新增非必要功能。

优先级:
P0 数据安全与权限
P1 核心流程和事务
P2 SQL 与性能
P3 前端体验

必须检查:
1. 预约→工单
2. 工单→结算
3. 结算主从表金额一致
4. 审批工作流
5. 我的申请/待办/已办
6. 流程图高亮
7. DataScope
8. Applicant Ownership
9. 禁止自审
10. BigDecimal
11. UNIQUE
12. oldStatus / oldPaidAmount
13. @Transactional 是否真实生效
14. N+1
15. 敏感信息是否提交到 Git

输出格式:
问题级别
文件
方法
问题原因
最小修改方案
验证步骤

三百六十、最终项目自检表

后端

[ ] Controller 无大段业务逻辑

[ ] Service 事务正确

[ ] DTO 参数校验

[ ] VO 不暴露敏感字段

[ ] Entity 不直接接收所有前端字段

[ ] 状态使用枚举

[ ] 金额 BigDecimal

[ ] Mapper 参数正确

[ ] SQL 无明显 N+1

[ ] 敏感接口有 @PreAuthorize

[ ] DataScope / Ownership 完整

三百六十一、数据库

[ ] 主键

[ ] 唯一索引

[ ] 外键逻辑关系清楚

[ ] DECIMAL 精度正确

[ ] 常用查询索引

[ ] 金额一致性 SQL 通过

[ ] 无重复结算

[ ] 无重复审批记录

[ ] 无超额支付

三百六十二、工作流

[ ] BPMN 已部署

[ ] process key 正确

[ ] businessKey 正确

[ ] processInstanceId 回写

[ ] CandidateGroup = roleKey

[ ] claim 正常

[ ] approve/reject 正常

[ ] History 正常

[ ] 流程图历史版本正确

[ ] 禁止自审

三百六十三、前端

[ ] API 地址正确

[ ] 字段名一致

[ ] Loading

[ ] 表单校验

[ ] 状态 Tag

[ ] 按钮权限

[ ] Drawer 正常

[ ] ProcessDiagram 正常

[ ] Timeline 正常

[ ] 金额格式统一

三百六十四、安全

[ ] 不能查看其他门店数据

[ ] 不能查看别人申请

[ ] 不能审批别人领取的 Task

[ ] 不能自己审批自己申请

[ ] 前端不能改金额

[ ] 前端不能改 approvalStatus

[ ] 前端不能传 applicantUserId 生效

[ ] 前端不能伪造 businessId

[ ] processInstanceId 查询有授权

三百六十五、演示

[ ] 测试账号准备

[ ] 测试数据准备

[ ] BPMN 已部署

[ ] 数据库备份

[ ] 后端能启动

[ ] 前端能启动

[ ] 完整链路跑通

[ ] Git Tag

三百六十六、本项目最终知识树

Taochewan
│
├─ Foundation
│  ├─ RuoYi
│  ├─ SpringBoot
│  ├─ MyBatis
│  ├─ MySQL
│  └─ Vue3
│
├─ Customer
│  ├─ Customer
│  └─ Vehicle
│
├─ Appointment
│  ├─ Reservation
│  ├─ Status
│  └─ Convert WorkOrder
│
├─ WorkOrder
│  ├─ Technician
│  ├─ WorkOrderItem
│  ├─ Snapshot
│  └─ Lifecycle
│
├─ Service
│  ├─ ServiceItem
│  ├─ Package
│  ├─ PackageItem
│  └─ Many-to-Many
│
├─ Settlement
│  ├─ Settlement
│  ├─ SettlementItem
│  ├─ BigDecimal
│  ├─ Discount
│  ├─ Receivable
│  └─ Payment
│
├─ Workflow
│  ├─ BPMN
│  ├─ ProcessDefinition
│  ├─ ProcessInstance
│  ├─ Task
│  ├─ History
│  ├─ ApprovalRecord
│  └─ Diagram Highlight
│
├─ Permission
│  ├─ Authentication
│  ├─ @PreAuthorize
│  ├─ DataScope
│  ├─ Ownership
│  └─ Task Identity
│
├─ Consistency
│  ├─ Transaction
│  ├─ UNIQUE
│  ├─ oldStatus
│  ├─ oldPaidAmount
│  └─ Snapshot
│
├─ Performance
│  ├─ Index
│  ├─ EXPLAIN
│  ├─ Pagination
│  ├─ Batch
│  └─ N+1
│
└─ Engineering
   ├─ Git
   ├─ Logging
   ├─ Exception
   ├─ Cursor Review
   ├─ Testing
   └─ Deployment Readiness

三百六十七、整套项目最重要的 20 条原则

1. 前端永远不是最终可信数据源

2. 金额必须后端 BigDecimal 计算

3. 数据库金额使用 DECIMAL

4. 历史业务必须使用快照

5. 主表金额从明细汇总

6. 状态按不同生命周期拆字段

7. 状态更新尽量带旧状态条件

8. @RepeatSubmit 不是最终幂等保证

9. UNIQUE 是重要数据库兜底

10. 事务必须真实生效,不只是写了注解

11. @PreAuthorize 控制动作

12. DataScope 控制数据范围

13. Ownership 控制本人数据

14. Task Assignee/CandidateGroup 控制流程任务

15. 申请人不能审批自己的申请

16. businessKey 关联流程与业务

17. 历史流程必须加载自己的 Definition 版本

18. 已办使用 HistoryService

19. 不直接操作 Activiti ACT_* 表

20. 先保证正确和安全,再谈性能和 UI

三百六十八、项目最终总结

淘车湾项目最值得你掌握的:

不是:

某一个 Controller 怎么写

而是:

一个真实业务系统
如何从需求
变成数据表
变成状态
变成事务
变成权限
变成流程
最后变成可维护代码

三百六十九、从初学者角度再总结

你可以把整个项目理解为五件事:

第一:
谁在操作?
→ 用户、角色、权限

第二:
操作什么?
→ 预约、工单、结算

第三:
数据现在是什么状态?
→ 状态机

第四:
多个表必须一起成功怎么办?
→ 事务

第五:
需要别人审批怎么办?
→ Activiti

三百七十、再往上一层

真正企业开发一直在处理:

数据正确

业务正确

权限正确

并发正确

历史可追踪

这五点比:

单纯会写 CRUD

重要得多。


三百七十一、本章最终验收

完成本章后,你应该能够:

1. 讲清淘车湾完整业务

2. 讲清数据库关系

3. 讲清快照机制

4. 讲清状态机

5. 讲清结算金额

6. 讲清事务

7. 讲清 RBAC

8. 讲清 DataScope

9. 讲清 Ownership

10. 讲清 Activiti

11. 讲清 businessKey

12. 讲清待办/已办

13. 讲清流程图高亮

14. 排查金额 Bug

15. 排查权限 Bug

16. 排查事务 Bug

17. 排查流程 Bug

18. 排查 N+1

19. 设计合理索引

20. 使用 EXPLAIN

21. 处理重复提交

22. 处理支付并发

23. 做完整回归测试

24. 用 Cursor 做全项目审查

25. 完成项目答辩

26. 把项目写进简历

三百七十二、下一课程

按照课程表,淘车湾项目实战到这里正式完成。

下一课程进入:

《企业开发流程》

下一篇会从:

真实公司项目是怎么开发的

开始讲:

需求
↓
产品原型
↓
需求评审
↓
技术方案
↓
数据库设计
↓
接口设计
↓
任务拆分
↓
Git 分支
↓
开发
↓
Code Review
↓
联调
↓
测试
↓
Bug 修复
↓
上线
↓
回滚
↓
复盘

也会专门讲:

程序员在公司一天到底在做什么

需求来了先做什么

什么时候建分支

怎么提 Commit

怎么提 PR

怎么和前端联调

怎么和测试沟通

Bug 单怎么看

为什么不能直接改生产数据库

上线前检查什么

出事故怎么回滚

这会把前面学到的 Java 技术,
进一步连接到:

真正的企业开发工作方式