
简介这份昌吉州医保结算系统实施版资料包面向医保信息化实施人员、医疗机构接口开发与运维学习者聚焦参保信息管理、费用审核报销、智能审核规则、HIS接口对接及跨区域结算等业务场景可用于了解地方医保结算流程、系统构成与实施要点。压缩包共3个文件包含1个htm说明页、1个txt下载说明和1个pbl系统库文件整体约929KB便于快速查阅安装指引、功能说明与程序结构线索。已有1837人学习下载在医保结算实施与入门参考方面具有一定关注度。读者可借助说明文档梳理系统接入与日常结算事务结合pbl文件观察实施版程序组织为医保政策落地、费用审核和接口联调提供参考适合作为地方医保项目资料归档与学习样本。1. 医保结算系统源码包一笔门诊结算要过几道关很多人对医保结算的想象是「总金额乘一个报销比例」真拿一张门诊处方拆开跑要在目录匹配、甲乙丙分类、起付线累计、封顶线截断、逐条取整这五道关口各走一遍任何一道顺序错了最终金额就差几块到几十块。这份源码包就是把这些关口摊开写清楚的实现参保人参保信息、药品/诊疗/耗材三大目录、报销政策配置、结算单生成、冲正退费、日终对账一条链路从接口到落库都能在本地跑起来。适合三类人要接手计费模块的后端、想给自家产品补一个规则引擎的开发者、需要给团队做技术预研的负责人。它不是只给架构图的样板规则表、状态机、幂等控制都有对应可执行代码。2. 目录与数据模型先把六张核心表和模块边界摸清拿到压缩包后第一件事不是急着mvn spring-boot:run而是先看清模块切分和表结构。原因是结算类系统最容易出问题的地方都藏在模块边界上规则层和记账层混在一起改一次政策要动三四个 Service目录层不区分版本历史单据重算就会漂移。先花二十分钟把边界摸清后面省下来的排查时间是以天计的。2.1 模块边界规则层为什么必须独立成无状态模块包里默认给的是 Java 主栈Spring Boot 加 MyBatis 加 MySQL前端是 Vue3 加 Element Plus规则层单独抽了一个module-rule。这个拆法的关键不在于分层好看而在于规则模块是无状态的输入一个结算上下文参保人、就诊类型、业务日期、政策版本、已累计额度输出一组明细计算结果中间不查库、不写库、不碰事务。insurance-settlement/ ├── module-common/ # 枚举、金额工具类、脱敏注解 ├── module-catalog/ # 三大目录与目录版本管理 ├── module-rule/ # 报销规则引擎纯计算、无状态 ├── module-settle/ # 结算主流程、状态机、幂等控制 ├── module-account/ # 冲正、退费、日终对账 └── web-api/ # 对外接口层只做参数校验与组装无状态带来的直接好处是可回放。线上出现金额争议时把当时的上下文 JSON 存下来直接喂给规则模块重算结果必须完全一致如果规则层里夹着select查当前目录、查当前政策重算结果一定和当时不一样因为你查的是「现在」而不是「当时」。我一般会要求规则模块对外只暴露一个SettleCalculator#calc(SettleContext)方法任何依赖都从上下文传进去不允许在计算过程里发 SQL。module-catalog负责目录版本module-settle负责状态流转和落库module-account只处理反向业务。三者的调用方向是单向的settle 依赖 rule 和 catalogaccount 依赖 settle反过来一律不允许。这条约束建议在 CI 里用依赖检查插件卡住否则半年后一定会有人图省事从 rule 里反向调 settle 的 Mapper。2.2 六张核心表从参保人到结算单的完整链路表不在多在于每张表的职责不能被侵蚀。包里核心是六张表加两张辅助表操作日志、对账批次下面是关键字段和设计意图。表名作用关键字段设计意图member参保人档案member_no、insure_type、annual_cap_used年度已用额度单独存避免每次结算全量汇总catalog_item三大目录项catalog_code、charge_class、class_self_ratio甲乙丙分类与乙类先行自付比例挂在目录上catalog_version目录版本version、effect_start、effect_end按业务日期取生效版本保证历史可追溯rule_policy报销政策version、deductible、ratio、annual_cap政策即数据改政策不改代码settle_order结算单主表order_no、idem_key、total_amount、fund_pay、status金额全部存分幂等键加唯一索引settle_detail结算明细order_id、catalog_code、fund_pay、self_pay逐条留痕尾差调整也落在这里结算单主表的建表语句值得单独看一眼几个细节后面几章都会用到。CREATE TABLE settle_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 结算单号业务唯一, idem_key VARCHAR(64) NOT NULL COMMENT 幂等键就诊号结算类型业务日期, member_id BIGINT NOT NULL, visit_type TINYINT NOT NULL COMMENT 1门诊 2住院, biz_date DATE NOT NULL COMMENT 业务日期不是创建时间, total_amount BIGINT NOT NULL COMMENT 总金额单位分, fund_pay BIGINT NOT NULL COMMENT 基金支付单位分, self_pay BIGINT NOT NULL COMMENT 个人自付单位分, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待结算 1已结算 2已冲正 3已退费, policy_ver VARCHAR(20) NOT NULL COMMENT 政策版本出账后必须可追溯, catalog_ver VARCHAR(20) NOT NULL COMMENT 目录版本同上, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_idem (idem_key), UNIQUE KEY uk_order_no (order_no), KEY idx_member_biz (member_id, biz_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;金额字段统一BIGINT存分这条如果一开始不定死后面全是血泪。biz_date和created_at是两个概念业务日期由就诊时间决定创建时间是落库时间跨零点结算时两者可能差一天政策和目录版本必须按biz_date取。policy_ver和catalog_ver冗余在主表上是刻意的出账之后的单据要想说清楚「当时按什么算的」只能靠这两列。uk_idem是幂等的最后一道防线第 4 章会细说。2.3 本地跑通三步冒烟测试与参数含义环境要求不复杂JDK 17、Maven 3.8 以上、MySQL 8.0、Redis 6 以上只用做幂等前置缓存和字典缓存不做持久化。下面三步走完你会得到一笔完整的门诊结算结果。# 1. 建库并导入表结构与样例数据 mysql -uroot -p123456 -e CREATE DATABASE IF NOT EXISTS insurance DEFAULT CHARSET utf8mb4 mysql -uroot -p123456 insurance sql/schema.sql mysql -uroot -p123456 insurance sql/init_catalog.sql # 三大目录样例含甲乙丙各若干条 mysql -uroot -p123456 insurance sql/init_policy.sql # 门诊/住院两套示例政策导入顺序不能反init_catalog.sql里带了外键约束会校验catalog_version是否存在。样例目录数据量不大三十来条但特意覆盖了甲类、乙类带先行自付比例、丙类三种情况方便你验证分类逻辑。# 2. application-local.yml 里必须对齐的三处配置 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/insurance?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 settle: policy-version: P20240101 # 必须与 rule_policy.version 完全一致否则规则加载为空 catalog-version: C20240101 # 同上对不上会在目录匹配阶段抛 CatalogNotFoundException rounding-mode: HALF_UP # 取整方式全链路只允许在这里改 amount-unit: CENT # 分切成 YUAN 只影响出参展示不动库policy-version和catalog-version这两项是新手上手时最常翻车的地方配置文件和库里的版本号差一个字符启动不报错一调接口才抛异常而且异常信息里往往只有编码没有版本看着很懵。建议在启动时加一个ApplicationRunner校验这两项在库里存在早失败远比晚失败省事。# 3. 起服务并打一发门诊结算 mvn -pl web-api -am spring-boot:run curl -X POST http://127.0.0.1:8080/api/settle/outpatient \ -H Content-Type: application/json \ -H X-Idem-Key: OP20240115000001-OUT-20240115 \ -d { memberId: 1001, visitNo: OP20240115000001, visitType: 1, bizDate: 2024-01-15, items: [ {catalogCode: DRUG001, qty: 2, unitPrice: 1250}, {catalogCode: TREAT007, qty: 1, unitPrice: 8000} ] }几个参数单独说明。unitPrice单位是分1250 表示 12.50 元前端传元、后端转分这一步建议放在接口层的 DTO 转换里别让规则模块去猜单位。X-Idem-Key是幂等键格式建议「就诊号-结算类型-业务日期」同一个键重复提交必须返回第一次的结果而不是新建一张单。bizDate不传会默认取服务器当天跨零点场景下这就是个坑所以样例里强制要求显式传。正常返回会包含orderNo、totalAmount、fundPay、selfPay以及一组明细。拿样例数据算一遍甲类药品 25.00 元全额进范围乙类诊疗 80.00 元按 10% 先行自付剩下 72.00 元进范围再扣起付线、乘比例、封顶截断。如果你算出来的数和返回对不上先别怀疑代码回头检查init_policy.sql里的起付线和比例是不是被你改过。3. 报销规则引擎起付线、封顶线与三大目录怎么写成代码结算系统的复杂度不在并发量在规则。同样一笔费用换个参保类型、换个就诊类型、换个目录版本结果就完全不同而这些组合在真实业务里是成百上千的。把规则写成一堆if-else的结局是政策一调整就要改代码、发版、回归改一次错一次。这一章讲清楚包里是怎么把政策变成数据、再把数据变成计算的。3.1 规则表设计政策即数据代码只负责执行顺序rule_policy表把可变部分全部外置。核心字段不多但每个字段的含义必须在团队内对齐否则后面每个人理解都不一样。字段含义单位备注version政策版本号字符串与配置项、结算单冗余字段一致visit_type就诊类型枚举1 门诊、2 住院同一版本下各一行deductible起付线分按年度累计不是每次重新扣ratio报销比例万分比存 8500 表示 85%避免浮点annual_cap年度封顶线分基金支付累计上限class_b_self_ratio乙类先行自付比例万分比也可放目录表包里放目录表更灵活比例用万分比存整数是个小决定但很关键。用0.85这种小数存库读出来再乘一不小心就是精度问题存8500计算时先乘后除全程整数运算只在最后一步做取整。起付线按年度累计这一点也常被写错不是每笔结算重新扣一次起付线而是要把当年已进入范围的金额累加起来只有当累计额跨过起付线之后的部分才开始报销。3.2 计算顺序一条明细从原价到个人自付的七个动作顺序是这个模块里唯一不能商量的事情。换任何一个步骤的先后结果都会变。包里固化的顺序是目录匹配 → 金额计算 → 甲乙丙分类 → 乙类先行自付 → 起付线抵扣 → 比例报销 → 封顶线截断 → 尾差调整。public SettleDetailResult calc(SettleContext ctx, OrderItem item) { // 1. 目录匹配同一编码可能有多版本按业务日期取生效版本不是取最新版本 CatalogItem catalog catalogService.match(item.getCatalogCode(), ctx.getBizDate()); // 2. 原始金额全程整数分 long amount item.getUnitPrice() * item.getQty(); // 3. 甲乙丙分类丙类全额自付不进基金也不参与起付线累计 if (catalog.getChargeClass() ChargeClass.C) { return new SettleDetailResult(amount, 0L, amount, amount); } // 4. 乙类先行自付先按比例切出一块个人自付剩下的才进报销范围 long preSelf Math.round(amount * catalog.getClassSelfRatio() / 10000.0); long inScope amount - preSelf; // 5. 起付线只用进入范围的金额去抵扣剩余起付线额度 Policy p policyService.get(ctx.getPolicyVersion(), ctx.getVisitType()); long remainDeductible Math.max(0L, p.getDeductible() - ctx.getAccumulatedInScope()); long afterDeductible Math.max(0L, inScope - remainDeductible); // 6. 比例报销 long fundPay Math.round(afterDeductible * p.getRatio() / 10000.0); // 7. 封顶线截断按年度已用额度算剩余别用本次结算金额去比封顶线 long capLeft Math.max(0L, p.getAnnualCap() - ctx.getAnnualFundUsed()); fundPay Math.min(fundPay, capLeft); return new SettleDetailResult(amount, fundPay, amount - fundPay, preSelf); }几个容易读漏的点。第 3 步丙类直接返回是因为丙类既不能报销也不应该占用起付线累计额度很多实现把它算进inScope再靠比例 0 兜底结果起付线被提前吃掉。第 4 步的除数是10000.0因为比例按万分比存这里用 double 只做一次性中间运算最终Math.round回到整数分不会累积误差如果你介意可以改成amount * ratio / 10000的整数除法加余数处理包的注释里给了这种写法。第 5 步的ctx.getAccumulatedInScope()是这一年度的累计进入范围金额必须在结算主流程里查出来放进上下文不能在规则模块里查库。第 7 步的封顶线判断要用「年度已用基金额度」而不是「本次基金支付」这两个搞混是新人最常见的错误之一会导致封顶线形同虚设。3.3 取整与尾差为什么明细合计必须等于结算单合计逐条取整必然产生尾差。两条明细各 0.5 分各自四舍五入上去汇总就比真实值多 1 分如果结算单合计是「先汇总再取整」算出来的那明细加起来就对不上主表。财务侧看到这种差异会直接打回所以必须在生成明细之后做一次强制对齐。// 明细逐条取整后把差额调整到基金支付金额最大的一条上 long detailSum details.stream().mapToLong(SettleDetailResult::getFundPay).sum(); long diff order.getFundPay() - detailSum; if (diff ! 0L) { SettleDetailResult target details.stream() .max(Comparator.comparingLong(SettleDetailResult::getFundPay)) .orElseThrow(() - new IllegalStateException(明细为空)); // 差额可能是负数直接对冲同时同步调整该条的个人自付 target.setFundPay(target.getFundPay() diff); target.setSelfPay(target.getSelfPay() - diff); }为什么调到金额最大的一条而不是最后一条因为尾差通常是几分钱调到最大的一条上相对误差最小也不会出现某条明细基金支付变成负数的尴尬场面。调整完之后必须再断言一次sum(detail.fundPay) order.fundPay且sum(detail.selfPay) order.selfPay断言失败直接抛异常回滚别让脏数据落库。还有一条经验取整方式HALF_UP/HALF_EVEN/ 向下取整全链路只允许配置一处。我见过一个项目规则模块用HALF_UP、对账模块用向下取整跑了一个月才发现每天的差异汇总里都有几毛钱的缺口查了两天才定位到。这种问题不难修难的是发现。4. 结算事务与状态机幂等、冲正、退费三件事怎么做扎实结算写库这段是整个系统里最容易出生产事故的地方。用户手抖点两下、网关超时重试、定时任务重跑任何一个都能造出重复结算单退费流程写错账目对不上查起来要翻好几天的日志。这一章把包里这三块的实现摊开讲。4.1 状态机四个状态与五条合法迁移状态不能随手update必须收敛到一张迁移表里任何不在表里的迁移一律拒绝。包里定义的状态是0 待结算、1 已结算、2 已冲正、3 已退费。当前状态允许迁移到触发动作说明0 待结算1 已结算结算成功唯一入口写主表与明细同事务1 已结算2 已冲正冲正原单不再参与对账1 已结算3 已退费退费部分退费另生成退费单不改原单金额2 已冲正1 已结算重新结算仅限当日冲正重结跨日走新单3 已退费——终态不允许再变public enum SettleStatus { PENDING(0), SETTLED(1), REVERSED(2), REFUNDED(3); private final int code; // 合法迁移白名单新增迁移必须显式加进来并补单测 private static final MapSettleStatus, SetSettleStatus ALLOWED Map.of( PENDING, Set.of(SETTLED), SETTLED, Set.of(REVERSED, REFUNDED), REVERSED, Set.of(SETTLED), REFUNDED, Set.of() ); public void checkTransition(SettleStatus target) { if (!ALLOWED.getOrDefault(this, Set.of()).contains(target)) { throw new IllegalStateException(非法状态迁移: this - target); } } }白名单写法比一堆if好维护的地方在于评审时一眼能看完所有迁移路径谁想偷偷加一条「已退费 → 已结算」的捷径代码 review 时必然会被看到。状态更新语句本身也要带条件update settle_order set status 1 where id ? and status 0靠影响行数判断是否抢到了这次迁移影响行数为 0 说明状态已经被别人改过直接走失败分支。4.2 幂等唯一索引兜底前置查询只是优化幂等要做三层缺一层都不算稳。第一层是接口层的X-Idem-Key进服务先查 Redis 有没有处理中的标记第二层是落库前的select ... where idem_key ?第三层是数据库唯一索引uk_idem这层是唯一可信的兜底前两层只是减少无效写入。Transactional(rollbackFor Exception.class) public SettleResponse settle(SettleRequest req, String idemKey) { // 第一层Redis 占位SETNX 失败说明有并发请求正在处理同一个键 Boolean locked redis.opsForValue().setIfAbsent(settle:idem: idemKey, 1, Duration.ofMinutes(10)); if (Boolean.FALSE.equals(locked)) { return queryAndReplay(idemKey); // 回放已有结果不是抛错 } try { // 第二层查库命中直接回放避免重复计算 SettleOrder exist orderMapper.selectByIdemKey(idemKey); if (exist ! null) { return SettleResponse.from(exist); } SettleOrder order doSettle(req, idemKey); return SettleResponse.from(order); } catch (DuplicateKeyException e) { // 第三层并发下唯一索引冲突回查后回放绝不重试插入 SettleOrder exist orderMapper.selectByIdemKey(idemKey); if (exist null) throw e; return SettleResponse.from(exist); } finally { redis.delete(settle:idem: idemKey); } }这段的关键在DuplicateKeyException的处理方式捕获后必须回查并回放结果而不是重试。重试会再撞一次唯一索引日志里刷一片异常业务侧还是一次失败。另外finally里删 Redis 键要小心如果执行时间超过 10 分钟锁已经自然过期删的是别人的锁生产上建议把 value 存成请求唯一标识删除前比对。还有一种情况要考虑同一个幂等键第一次请求还在事务里没提交第二次请求进来查库查不到插入时撞唯一索引 —— 这时候回查可能也查不到未提交不可见。包里对这个的处理是回查加重试三次、每次间隔 200ms超过就返回「处理中请稍后查询」让前端轮询。这个妥协比死等事务锁要好得多。4.3 冲正与退费只做反向记账永不改历史冲正和退费是两个概念代码里必须分开。冲正是指原结算单整体作废算错了、重复收的退费是指患者实际退掉了部分或全部服务。两者的共同点是原单的金额字段一个都不改靠新增反向单据来对冲。-- 冲正原单置为已冲正新增一张负数结算单两单永远成对出现 UPDATE settle_order SET status 2 WHERE id #{originId} AND status 1; INSERT INTO settle_order (order_no, idem_key, member_id, visit_type, biz_date, total_amount, fund_pay, self_pay, status, policy_ver, catalog_ver) SELECT CONCAT(REV, order_no), CONCAT(REV-, idem_key), member_id, visit_type, biz_date, -total_amount, -fund_pay, -self_pay, 1, policy_ver, catalog_ver FROM settle_order WHERE id #{originId};反向金额用负数存好处是日终对账时直接sum(fund_pay)就自动对冲了不需要在报表逻辑里区分正负单据。退费则更细按明细退生成一张退费单并把status置为 3退费金额累加到参保人的年度已用额度上要做减法但不能超过已用额度这点必须在校验里卡住否则退费退多了会把年度额度算成负数后面所有结算全乱。事务边界上冲正和退费都必须在一个事务里完成「置原单状态 插反向单 回滚参保人额度」三件事锁的顺序统一按member → settle_order来避免两个并发冲正互相死锁。我一般会在member表上加一个版本号字段乐观锁冲正时带上where version ?冲突了就让业务重试比悲观锁省心。5. 避坑排查上线前必须自己踩一遍的五个坑这一章的五个坑都是真跑起来才会暴露的光看代码看不出来。建议在做预研或交付前按顺序手动复现一遍比读十遍文档有用。5.1 一分钱对不上BigDecimal 和 double 混着用现象单条明细单独算是对的多条明细汇总后结算单总额比明细合计多 1 分或 2 分财务对账每天都有零星差异。原因规则层用的是整数分但某个中间环节为了「精确」换成了BigDecimal或者double两次取整叠加。更隐蔽的是new BigDecimal(0.1)这种构造方式本身带进来的就是二进制误差。解决全链路只允许一种数值载体。包里定的是long存分比例用万分比整数只在最后一步Math.round。如果业务确实需要BigDecimal构造必须用字符串new BigDecimal(0.1)并且setScale(0, RoundingMode.HALF_UP)只能在出明细时调用一次。上线前加一条断言sum(detail) order不成立直接抛异常。5.2 目录版本切换当天历史单据金额变了现象目录版本更新后的第二天查询上个月的结算单重新打印金额和当时不一样了。原因查询或重算时直接取「当前生效版本」没有用结算单上冗余的catalog_ver。这在只读展示时通常无害但只要涉及重新计算或对账结果一定漂移。解决一切重算入口都从settle_order.catalog_ver和policy_ver读版本作为参数传给规则模块不允许在规则模块内部查「最新版本」。同时给catalog_version加上effect_start/effect_end双端区间匹配时用biz_date between effect_start and effect_end别只判 now()那样会出现两个版本同时生效的窗口。5.3 同一笔结算被点了两次生成两张单现象用户反馈「我就点了一次」库里同一就诊号有两张已结算单金额一样基金被重复记账。原因前端防重复提交做了但接口被网关重试绕过了或者 Redis 锁的 key 里拼了 UUID每次请求都不一样等于没锁。解决幂等键必须来自业务不能来自请求本身。格式统一为「就诊号-结算类型-业务日期」由调用方传或者服务端按就诊号生成。三层防护要全上尤其是数据库唯一索引这一层没有它前面两层都是纸糊的。补充一点DuplicateKeyException必须显式捕获并回放否则用户看到的是 500然后他会再点一次。5.4 跨零点结算政策日期取错现象晚上 23:58 挂号的急诊结算发生在次日 00:03结果套用了次日的政策版本或者年度额度被算进了新的一年。原因代码里用了LocalDate.now()或者new Date()作为业务日期。业务日期应该由就诊时间决定而不是处理器执行时间。解决所有涉及日期的地方统一从入参bizDate取服务端只做格式校验不做补全缺省值只在测试环境的配置里给。年度额度归属也按bizDate的年份算不按创建时间。这条在年初年尾特别容易炸12 月 31 日晚上那几笔跨零点的单子是每年必出的经典故障。5.5 日志里打了完整证件号出事才发现现象排查问题翻日志发现入参日志把参保人证件号、手机号全打出来了。原因用了log.info(req {}, req)这种整对象打印DTO 上没有脱敏注解。解决在module-common里定义Sensitive注解加自定义JsonSerializer对证件号、手机号、姓名做打码序列化时自动生效。同时把所有打印整个请求对象的日志改成打印关键字段。这件事平时看着多余等真出事的时候它就是后悔药。6. 回归验证用构造数据集把结算结果钉死规则类系统最怕的不是写错是改错。今天调了一下起付线逻辑明天加了一个乙类比例如果没有一套固定的回归数据集你永远不知道自己动坏了哪条链路。包里带了src/test/resources/cases目录一份 YAML 描述一个场景配一个期望结果跑单测就是全量回归。我的做法是先自己造五条覆盖边界的用例把期望值手算出来写死在 YAML 里然后再改代码。这五条是正常门诊跨越起付线、乙类先行自付、起付线未达基金支付应为 0、封顶线截断基金支付到顶、冲正后退费额度回滚。用例关键输入期望基金支付验证点正常门诊甲类 200 元起付线 50 元比例 85%12750 分起付线抵扣正确乙类先行自付乙类 100 元先行自付 10%按 90 元进范围计算自付切分正确起付线未达甲类 30 元起付线 50 元0不越线不报销封顶线截断年度已用接近封顶剩余额度不会超年度上限冲正退费原单 12750 分全额冲正0反向单对冲干净跑回归的方式很简单mvn -pl module-rule test就能跑完所有用例规则模块无状态的设计在这里体现价值不需要起 Spring 上下文不需要连库纯计算几秒钟跑完可以塞进每次提交前的钩子里。有条件的团队建议再加一条把线上近三个月已结算单的上下文导出来做回放重算结果与库中金额逐笔比对差异为 0 才算通过。这条比任何单测都有说服力因为它验的是真实数据分布。最后说个教训。早些年我改一个取整规则本地单测全绿因为用例都是整数金额上线后第二天对账发现几百笔差一分钱。从那以后我每次动金额相关的代码都强制走一遍「构造五条边界用例 线上数据回放 明细合计断言」这三步一步都不省。规则引擎这东西快是快但它不会提醒你哪里疼只能靠你自己提前扎针。希望帮到你。本文还有配套的精品资源点击获取