对android M中Call的概要总结(目录结构/界面组成/call状态转化上报/常见log分析)

本文深入解析Android通话框架,涵盖Call的目录与框架结构、InCallUI层架构、Call状态及上报流程等内容,帮助读者理解通话界面与底层实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


主要内容
1. Call涉及的目录结构及框架结构
2. InCallUI层的基本架构(所涉及的Presenter、Fragment及Activity)
3. Call的几种状态(对应phone状态)及上报流程
4. GSM与IMS MO流程的差异
5. 分析问题的常用log

希望你在看完本篇以后能够:
1.快速找到Call界面某个小时内容对应的fragment及presenter
2.结合log快速定位当前call的状态
PS:内容均为博主个人经验总结,如有出错的地方欢迎指正。

1. Call涉及的目录结构及框架结构

1.1 目录结构

packages/ apps/Dialer/
packages/apps/InCallUI

packages/services/Telecomm
packages/ services/Telephony

framework/base/telecomm
framework/opt/telephony
(vendor/…/ ims Ims Call)

Dialer    
    拨打电话的入口,来电不会经过Dialer。但是拨打电话的出口不光是Dialer,在联系人和短信里也有拨打电话的出口。代码运行在dialer进程。
InCallUI
    负责显示通话界面的信息,来电信息。dialer进程。
Telecomm
    处理Intent,发送广播,设置call的状态,audio状态。system_process和telecomm:ui进程。
Telephony
    向下层传递拨号,注册了很多广播,申请很多权限(service data sms wap network)。 phone进程
telecomm 
    提供placeCall的接口(自android M开始),创建outgoingCall的connection,通知上层成功建立connection
telephony
    拨号 也就是dial命令的下发,但是如果是Ims网络就会有下面一步
Vendor/ims
创建ImsConnection,ImsCall,拨号。phone进程。

1.2 框架结构

这只是框架上的一个大致结构


在实际的流程中并不一定是自上而下或者 自下而上的,也有可能跳过某个模块直接 传递信息。
比如在Dialer拨号的时候,就是直接调用 framework/base/telecmm中 TelecomManager的placeCall接口拨打 电话。

2. InCallUI层的基本架构(所涉及的Presenter、Fragment及Activity)

2.1InCallUI内有多个对应的Presenter和Fragment

Presenter和Fragment的关系: Fragment直接控制界面上的控件,并处理一些简单的逻辑,对应的Presenter处理稍复杂的逻辑。
CallCardFragment
CallCarfPresenter
    CallButtonFragment
    CallButtonPresenter
CallCard和CallButton是绑定在一起显示的,有CallCard就一定会有CallButton
CallCard显示拨打的电话的信息,包括联系人姓名,号码,通话时长,通话类型等(稍后有展示),CallButton包括可以对当前通话进行的操作,如通话保持,通话静音,通话录音等。

VideoCallFragment
VideoCallPresenter
主要是视频内容,在进入退出视频模式时同时处理camera、audio相关信息。

AnswerFragment
AnswerPresenter
显示新来电,或者视频升级请求,重点在于对不同类型的通话显示不同的选项(也可根据运营商进行不同配置)。

DialpadFragment
DialpadPresenter
拨号盘,添加通话的时候显示。一般没什么问题,之前有该显不显的问题,同时dialpad的显示会影响endCallButton的大小,两者是同步变化的。
 
ConferenceManagerFragment
ConferenceMangerPresenter
没遇到过这里的问题,只是准备PPT的时候才看到这个。


InCallActivity启动的时候,在显示完动画后,调用showCallCardFragment()显示CallCardFragment(CallCardFragment本身也有动画)然后Fragment内new Presenter。
其他几个也有各自的显示流程,不一定是在InCallActivity的动画显示后立刻show出来。
另: VideoCallFragment不是通过 showFragment()显示的。

2.2 通话界面布局分析 

注:界面被轻量定制,并非和原生完全一样。


图1 CallCardFragment和CallButtonFragment
图中除了priamry_card_info.xml和call_button_fragment.xml是布局文件,其他标注均为控件id。
AnswerFragment和DialpadFragment共用一个FramLayout
@+id/answer_and_dialpad_container
在显示的时候覆盖在CallCard的上层,比如CallCard中的photo(联系人头像)就是被dialpad给覆盖。

图2 VideoCallFragment 真人出境啦
左侧为普通视频电话,右侧为视频会议电话。
注意,虽然右侧看起来像是有4个小窗口显示内容,但实际上和左边一样只有两个控件显示视频,只不过右侧上方的视频内容被分成了3块。我们把黑色的背景色调成蓝色,就容易区分出是两个控件了。
至于为什么两张图incomingVideo的位置不一样,CallCard一个显示一个没显示,是因为,点击屏幕(或者等5s)后CallCard会隐藏,然后incomingVideo会自动调整位置到中央。

 
图3 AnswerFragment
 更多的界面就不一一详细说明了。
如果你看到了不常见的控件,那它在代码中的位置,和临近位置的控件的代码位置也是临近的(有点拗口= =)。
 

2.3 InCallPresenter和InCallActivity

这个为什么要单独列成一条呢?因为这个Presenter比前面几个Presenter都重要,它连结这界面的各个部分。
InCallPresenter
从CallList中获取更新然后通知到InCallActivity,由InCallActivity控制界面显示。
同时各个Fragment和Presenter可以获得InCallPresenter的实例,进而更新/获得数据。 单独的Presenter负责对应的界面的逻辑,InCallPresenter负责整个界面的显示,协调各个Fragment/Presenter之间的工作。
以视频电话全屏为例,在VideoCallPresenter中有隐藏CallCard进入全屏模式的需求,在VideoCallPresenter中会通过InCallPresenter调用CallCardPresenter的onFullscreenModeChanged()方法,进而调用CallCardFragment的方法隐藏CallCard
InCallActivity
InCallActivity是InCallUI的主体,可以说所有的类都是围绕着或者是为了这个Activity工作的, 各个Fragment附着在InCallActivity上,InCallPresenter通过InCallActivity提供的 get***Fragmment的方法,获得具体Fragment对象, 更新界面。
Fragment是通过InCallActivity创建和显示的,但是各个Presenter和InCallPresenter之间的交互 不一定全都经过InCallActivity(比如可以通过listener调用)
 
图4 InCallUI基本架构
我们举两个例子 来说明界面上更新走过的流程:
1.设置CallStateLabel上的文本,比如"requesting video",代码的执行经过InCallPresenter> CallCardPresenter> CallCardFragment,跳过了InCallActivity;
2.弹出dialog,比如“网络不可用” ,代码执行经过IncallPresenter > InCallActivity
还有另外一个比较重要的没有体现在图上的类叫做 TelecomAdapter。
它的职责主要是把界面上操作的命令向下层传递,具体点就是把对call的操作传给Telecomm,包括接听,挂断,拒接,静音,切换audioRoute等。

3. Call的几种状态(对应phone状态)及上报流程

3.1 Call的状态

下面这张图不用记,知道有这么多就行了。
不同模块的call的状态不一样,有些能对应上,有些是独有的对应不上的。
比较重要的是InCallUI里的 Call.State和InCallPresenter的内部类 InCallState
InCallPresenter控制着界面显示,这里的InCallState就是界面显示的依据。
TelephonyManager向外提供访问手机状态的接口 ,只有三种状态。


图5 Call的状态

下图表现的是InCallUI Call状态的转化以及对应的TelephonyManager.CALL_STATE_***状态

图6 InCallUI 中的Call状态转化
其中CONNECTING/SELECT_PHONE_ACCOUNT这两个是 二者取其一。如果是双卡手机并设置每次询问,则拨号后的状态为SELECT_PHONE_ACCOUNT,如果有是单卡或者默认某一张卡拨打,则状态为CONNECTING,并且这个状态很短暂,很快就转换为DIALING。
 

3.2 上报流程

都是以 UNSOL_RESPONSE_CALL_STATE_CHANGED消息上报为开始,
//CS

   
  1. 09-09 10:52:42.093 D/RILJ    ( 4017): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED [SUB1]//PS 07-18 15:31:58.120 
//PS

   
  1. D/ImsSenderRxr( 4499): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED  [id=1,DIALING,toa=129,norm,mo,0,voc,noevp,,cli=1,,3Call Details = 3 2 callSubState 0 videoPauseState2 mediaId0 Local Ability  Peer Ability  Cause code 0,CallFailCause Code= 501,CallFailCause String= null, ECT mask: 0] [SUB0]
流程中多次用到RegistrantList消息处理机制,比较关键的一个地方是TelephonyConnection里,在外拨(或者来电)设置连接的时候注册了一个消息
getPhone().registerForPreciseCallStateChanged(mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);当收到这个消息的时候会调用updateState()更新状态。
updateState
在Telephony更新call的状态的时候不同的状态对应不同的方法,如setActiveInternal()
 
图7 ims call state update(简图)
上图也印证了之前说的,虽然层次上packages叠在framework上面,但实际上他们并不是严格按照相邻的顺序去调用。有可能隔着一层就调用了,也可能反复调用。
下图以IMS电话接通状态上报为例
(图略)
 图7 ims call state update(详图)

4. GSM与IMS MO流程的差异

  • 最大的区别是 IMS通话最后是通过高通私有代码ims.app中拨出去的。
  • IMS MO也要经过GSMPhone,判断到IMS网络可用以后会调用到ImsPhone中的dial()方法。
  • ImsSenderRxr.java相当于CS call中的RIL.java,DIAL的命令也是在此发送
      07-18 15:35:04.977 D/ImsSenderRxr( 4499): [0247]> DIAL[SUB0]
  • ImsPhone中的log是在radio log中打印,但是ImsSenderRxr的log是在main log打印。
  • 在ImsPhone的后续步骤中,会创建ImsPhoneConnection和ImsCall。
 
对比图
(图略)
图8 GSM MO和IMS MO 对比(注:34画错了,不该指向ImsPhone,请读者忽略)
箭头指向为对应步骤,可以很明显的看到Ims的MO流程在这部分更复杂一些。针对IMS网络下的通话,专门建立了ImsPhoneConnection ImsCall。

5. 分析问题的常用log

5.1InCallUI
前面说过InCallUI的界面显示是以InCallState的状态为根据的,而查看InCallState的关键字是 Phone switching state

   
  1. 14:42:29.721 I/InCall  (24960): InCallPresenter - Phone switching state: NO_CALLS -> NO_CALLS
  2. 14:42:29.864 I/InCall  (24960): InCallPresenter - Phone switching state: OUTGOING -> OUTGOING
  3. 14:42:30.262 I/InCall  (24960): InCallPresenter - Phone switching state: OUTGOING -> PENDING_OUTGOING
  4. 14:42:31.194 I/InCall  (24960): InCallPresenter - Phone switching state: PENDING_OUTGOING -> OUTGOING
  5. 14:42:34.975 I/InCall  (24960): InCallPresenter - Phone switching state: OUTGOING -> INCALL
  6. 14:42:35.630 I/InCall  (24960): InCallPresenter - Phone switching state: INCALL -> INCALL
InCallUI会根据这个状态显示界面信息。
这个状态一般是跟CallList里的call同步,但也有例外,就是会 撤回状态,关键字 Undo the state change
Log.i(this, "Undo the state change: " + newState + " -> " + mInCallState);
14:42:35.998 I/InCall  (24960): InCallPresenter – Undo the state change: INCOMING ->NO_CALLS
这时候就出现了界面显示和实际情况不一样,上面的log就对应来电没有界面。
注意 :INCALL包括DISCONNECTING和DISCONNECTED,虽然这两个是短暂的状态,但是看到的最后几个INCALL状态可能call已经断开了。

CallList中的onUpdate(),这是常见的看InCallUI中call的数量和状态的地方,关键字 CallList - onUpdate  

   
  1. 17:13:21.514 I/InCall  ( 4166): CallList - onUpdate - [Call_0, CONNECTING, [Capabilities:
  2. 17:13:21.568 I/InCall  ( 4166): CallList - onUpdate - [Call_0, DIALING, [Capabilities: CAP
  3. 17:13:29.997 I/InCall  ( 4166): CallList - onUpdate - [Call_0, ACTIVE, [Capabilities: CAPA
  4. 17:13:30.255 I/InCall  ( 4166): CallList - onUpdate - [Call_0, ACTIVE, [Capabilities: CAPA
  5. 17:13:39.896 I/InCall  ( 4166): CallList - onUpdate - [Call_1, CONNECTING, [Capabilities:]
  6. 17:13:43.179 I/InCall  ( 4166): CallList - onUpdate - [Call_1, CONNECTING, [Capabilities:
  7. 17:13:43.266 I/InCall  ( 4166): CallList - onUpdate - [Call_1, DIALING, [Capabilities: CA
  8. 17:13:43.451 I/InCall  ( 4166): CallList - onUpdate - [Call_1, DIALING, [Capabilities: CA
  9. 17:13:43.471 I/InCall  ( 4166): CallList - onUpdate - [Call_0, ONHOLD, [Capabilities: CAP
  10. 17:13:43.492 I/InCall  ( 4166): CallList - onUpdate - [Call_0, ONHOLD, [Capabilities: CAP
5.2 Telecom
另外自android 6.0开始,Telecomm加了一个关键字为 Event 的log,会打印在Telecom内执行的关键步骤的log。打印出来如下

   
  1. 17:22:05.385 I/Telecom (  769): Event: Call 8: CREATED, null
  2. 17:22:05.394 I/Telecom (  769): Event: Call 8: SET_CONNECTING, ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService},
  3. 17:22:05.449 I/Telecom (  769): Event: Call 8: AUDIO_ROUTE, EARPIECE
  4. 17:22:09.161 I/Telecom (  769): Event: Call 8: BIND_CS, ComponentInfo{com.android.phone
  5. 17:22:09.240 I/Telecom (  769): Event: Call 8: CS_BOUND, ComponentInfo{com.android.phone
  6. 17:22:09.241 I/Telecom (  769): Event: Call 8: START_CONNECTION, tel:*****
  7. 17:22:09.399 I/Telecom (  769): Event: Call 8: SET_DIALING, successful outgoing call
  8. 17:22:17.395 I/Telecom (  769): Event: Call 8: SET_ACTIVE, active set explicitly
  9. 17:23:03.636 I/Telecom (  769): Event: Call 8: REQUEST_DISCONNECT, null
  10. 17:23:04.211 I/Telecom (  769): Event: Call 8: SET_DISCONNECTED, disconnected set explicitly> DisconnectCause [ Code: (LOCAL) Label: () Description: () Reason: (LOCAL) Tone: (27) ]
  11. 17:23:05.612 I/Telecom (  769): Event: Call 8: DESTROYED, null
DisconnectCause  是常用的查看通话断开原因的log。  
注意:Telecom的log是在system_process进程打印,用adb命令抓log的时候要加上-b system
5.3 MT 来电
在main log中我常看

   
  1. 00:01:21.154 D/Telephony( 1389): PstnIncomingCallNotifier: handleNewRingingConnection
如果这条看不到还可以查CallsManager中的setCallState,可以从状态转化中看到来电状态。

   
  1. 14:42:34.888 I/Telecom ( 2920): CallsManager: setCallState DIALING -> ACTIVE, call: [100747014, DIALING,
5.4 MO DIAL
IMS(PS)

   
  1. 18:30:35.325 D/ImsSenderRxr( 3325): [0056]> DIAL[SUB0]
  2. 18:30:35.365 D/ImsSenderRxr( 3325): [0056]< DIAL [SUB0
有DIAL回传才表示拨号成功
GSM(CS)

   
  1. 17:29:56.206 D/RILJ    ( 4017): [6301]> DIAL [SUB1]
  2. 17:29:56.250 D/RILJ    ( 4017): [6301]< DIAL  [SUB1]
5.5 HANGUP

  
  1. 15:32:37.899 D/ImsSenderRxr( 4499): [0240]> HANGUP[SUB0]
  2. 15:32:37.995 D/ImsSenderRxr( 4499): [0240]< HANGUP [SUB0]
5.6 IMS
在IMS网络下通话的很多log是在main log中打印的,其中几个比较关键的类包括ImsSenderRxr、ImsServiceSub,以这两个类名为关键字在log中搜索就好了,或者以关键字/ImsSe把这两个一起搜出来


  
  1. 18:30:37.522 D/ImsSenderRxr( 3325): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED  [id=1,DIALING,toa=129,norm,mo,0,voc,noevp,,cli=1,,3Call Details = 3 2 callSubState 0 videoPauseState2 mediaId5 Local Ability  Peer Ability  Cause code 0,CallFailCause Code= 0,CallFailCause String= null, ECT mask: 0] [SUB0]
  2. 18:30:37.522 D/ImsServiceSub( 3325): Message received: what = 1
  3. 18:30:37.522 D/ImsServiceSub( 3325): >handleCalls
  4. 18:30:37.523 D/ImsServiceSub( 3325): handleCalls: dc = id=1,DIALING,toa=129,norm,mo,0,voc,noevp,,cli=1,,3Call Details = 3 2 callSubState 0 videoPauseState2 mediaId5 Local Ability  Peer Ability  Cause code 0,CallFailCause Code= 0,CallFailCause String= null, ECT mask: 0

希望阅读完对您有所帮助,也欢迎给出意见和建议。
 
//CS

   
  1. 10:52:42.093 D/RILJ    ( 4017): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED [SUB1]
//PS

   
  1. 15:31:58.120 D/ImsSenderRxr( 4499): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED  [id=1,DIALING,toa=129,norm,mo,0,voc,noevp,,cli=1,,3Call Details = 3 2 callSubState 0 videoPauseState2 mediaId0 Local Ability  Peer Ability  Cause code 0,CallFailCause Code= 501,CallFailCause String= null, ECT mask: 0] [SUB0]
<think>好的,我现在需要帮用户细分一下概要设计和详细设计。首先,我得回想一下用户之前的问题,他之前问过关于研究思路是否采用瀑布模型,现在又接着问概要设计和详细设计的细分,看来用户可能是在准备开题报告或者项目文档,需要明确这两个设计阶段的具体内容和区别。 用户可能是学生或者刚入行的研究人员,对软件工程中的设计阶段不太清楚,所以需要详细的解释。概要设计和详细设计在瀑布模型中确实是两个重要阶段,但用户可能不太了解它们各自的职责和产出物。我需要确保解释清晰,结构分明,同时结合之前的回答,保持一致性。 首先,我应该从定义出发,解释两者的基本概念。概要设计是高层设计,关注系统架构和模块划分,而详细设计则是具体实现细节。然后需要分点说明每个阶段的具体内容,比如在概要设计中会涉及架构图、模块接口、技术选型等,而详细设计则包括类图、数据库表结构、算法逻辑等。 还要注意用户之前提到的使用瀑布模型,所以需要结合瀑布模型的阶段来展示这两个设计阶段的位置,比如在需求分析之后,编码之前。可能需要用一些例子或公式来增强专业性,比如在接口设计中提到RESTful API的数学表达,或者数据库设计中用ER图和三范式。 此外,用户可能需要知道两者的区别和联系,避免混淆。所以应该有一个对比部分,用表格或者列表展示关键点,比如抽象级别、产出物、参与者等。同时,给出示例的表述结构,帮助用户在实际文档中应用这些概念。 最后,要检查是否符合之前的系统级指令,比如使用正确的中文,数学公式用$$包裹,行内公式用$,并且结构清晰。可能需要用代码块或示例来展示如何撰写这两个设计阶段的文档结构,确保用户能够直接参考使用。 总结一下,我的回答需要涵盖: 1. 概要设计和详细设计的定义和区别 2. 各自的具体内容和产出物 3. 在瀑布模型中的位置和作用 4. 示例结构和关键要素 5. 对比表格和实际应用建议 这样用户不仅能理解两者的细分,还能知道如何在实际项目中应用,满足他撰写开题报告或项目文档的需求。</think>以下是软件工程中**概要设计**与**详细设计**的细分对比与实施要点,结合科研项目/毕业设计的实际需求呈现: --- ### 一、核心差异总览 | **维度** | **概要设计(HLD)** | **详细设计(LLD)** | |------------------|---------------------------------------------|---------------------------------------------| | **抽象级别** | 系统级(黑盒视角) | 模块级/代码级(白盒视角) | | **核心目标** | 定义**系统如何工作** | 定义**模块如何实现** | | **典型产出物** | 架构图、模块划分、接口规范 | 类图、时序图、伪代码、DB表结构 | | **参与者** | 系统架构师、项目经理 | 开发工程师、测试工程师 | | **评审关注点** | 技术可行性、扩展性、模块耦合度 | 算法正确性、边界条件覆盖、性能指标 | --- ### 二、概要设计(High-Level Design)详解 #### 1. **核心任务** - 将需求转化为**可落地的技术框架**,回答“系统怎么做”而非“做什么” - 建立全局约束条件(如响应时间需满足$T_{avg} \leq 500\text{ms}$) #### 2. **关键交付物** - **系统架构图** *示例:* ```plaintext ┌─────────────┐ ┌─────────────┐ │ 客户端 │ HTTP │ API网关 │ │ (Vue.js) │◄─────►│ (Spring Cloud)│ └─────────────┘ └──────┬──────┘ │ REST ┌──────▼──────┐ │ 业务微服务 │ │ (Spring Boot)│ └──────┬──────┘ │ JDBC ┌──────▼──────┐ │ 数据库 │ │ (MySQL 8.0) │ └─────────────┘ ``` - **模块划分与接口定义** - 模块间调用关系(如订单服务调用库存服务的接口) - 接口协议示例: ```math \text{POST /api/inventory/deduct} \\ \text{Request Body:} \\ \{ \\ \quad "sku_id": "A1001", \\ \quad "quantity": 5 \\ \} \\ \text{Response Code: 200 OK / 409 Conflict} ``` - **技术选型论证** - 数据库选择依据:关系型 vs NoSQL(通过$CAP$定理分析) - 框架对比(如Spring Boot vs Quarkus的启动时间对比$t_{boot}$) --- ### 三、详细设计(Low-Level Design)详解 #### 1. **核心任务** - 将概要设计细化为**可直接编码的蓝图** - 确保满足非功能性需求(如并发量$QPS \geq 1000$的实现方案) #### 2. **关键交付物** - **类图与对象关系** ```plaintext +---------------------+ +---------------------+ │ Order │ │ OrderItem │ +---------------------+ +---------------------+ │ - orderId: String │◄───1..*──│ - itemId: String │ │ - userId: String │ │ - skuId: String │ │ - totalAmount: BigDecimal │ │ - quantity: int │ +---------------------+ +---------------------+ ``` - **数据库详细设计** - ER图与三范式验证 - 索引策略(如对`create_time`字段建立B+树索引) - 示例表结构: ```sql CREATE TABLE `order` ( `id` VARCHAR(36) PRIMARY KEY, `user_id` VARCHAR(20) NOT NULL, `total_amount` DECIMAL(12,2) CHECK(total_amount >= 0), INDEX `idx_user` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` - **算法逻辑描述** - 伪代码或数学公式(如库存扣减的原子操作): ```math \text{UpdateInventory}(sku, qty): \\ \quad \text{IF } stock[sku] \geq qty: \\ \quad \quad stock[sku] \leftarrow stock[sku] - qty \\ \quad \quad \text{Return SUCCESS} \\ \quad \text{ELSE}: \\ \quad \quad \text{Return FAILURE} ``` - **异常处理机制** - 定义错误码(如`4001: 库存不足`) - 重试策略(如指数退避算法:$t_{wait} = 2^{n} \times 100\text{ms}$) --- ### 四、科研项目中的典型表述结构 #### 1. **概要设计章节示例** ```markdown ### 4.1 系统概要设计 **4.1.1 架构设计** 采用分层架构,满足$$ \text{模块复用率} \geq 60\% $$: - 表现层:Vue.js + Element UI - 逻辑层:Spring Boot微服务(服务注册中心采用Nacos) - 数据层:MySQL 8.0 + Redis缓存(缓存命中率目标$H_{cache} > 85\%$) **4.1.2 核心模块划分** - 用户管理模块:实现RBAC权限模型 - 订单模块:状态机设计(包含$S = \{\text{待支付}, \text{已发货}, \text{已完成}\}$) - 库存模块:采用乐观锁解决超卖问题 ``` #### 2. **详细设计章节示例** ```markdown ### 4.2 库存模块详细设计 **4.2.1 数据库设计** - 库存表字段定义: ```sql CREATE TABLE `inventory` ( `sku_id` VARCHAR(20) PRIMARY KEY, `stock` INT UNSIGNED NOT NULL DEFAULT 0, `version` BIGINT NOT NULL DEFAULT 0 -- 乐观锁版本号 ); ``` **4.2.2 扣减逻辑实现** 采用CAS(Compare-And-Swap)算法: ```java public boolean deductStock(String skuId, int qty) { Inventory inv = inventoryDao.selectBySku(skuId); if (inv.getStock() < qty) return false; int rows = inventoryDao.update( "UPDATE inventory SET stock=stock-?, version=version+1 " + "WHERE sku_id=? AND version=?", qty, skuId, inv.getVersion() ); return rows > 0; } ``` ``` --- ### 五、评审要点建议 - **概要设计评审**:重点检查模块间通信成本(如RPC调用次数$N_{call} \leq 3$) - **详细设计评审**:验证算法时间复杂度(如排序模块需保证$O(n \log n)$) 需要具体模块的设计案例或工具推荐(如PlantUML绘图规范)可进一步补充说明。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值