废品回收平台开发资料

面向若依 RuoYi-Vue3 开发落地,包含表结构、接口定义、权限字典、Vue3 页面说明、税务额度口径和代码骨架。

开发文档包说明 README.md

废品回收平台若依开发文档包

适用技术栈假设:RuoYi-Vue3 / Vue3 / Vite / Element Plus / Spring Boot / MyBatis / MySQL。若实际采用 RuoYi-Cloud 或 RuoYi-Vue-Plus,可复用业务表与接口语义,按对应框架调整包名、权限注解、分页和代码生成模板。

文件清单

  • 01-refined-requirements.md:重新梳理后的需求与字段盘点。
  • 02-database-design.md:数据模型说明、表关系和状态字典。
  • 03-api-design.md:接口定义,按若依 Controller 模块划分。
  • 04-ruoyi-development-guide.md:若依开发落地说明。
  • 05-dict-and-permission.md:字典、菜单、权限标识建议。
  • 06-vue3-frontend-guide.md:RuoYi-Vue3 前端页面、路由、API 文件建议。
  • 07-tax-policy-and-quota.md:反向开票政策口径、税费与额度配置建议。
  • 08-backend-code-skeleton.md:若依后端核心代码骨架和开发任务拆分。
  • sql/recycle_platform_schema.sql:业务表结构 SQL。
  • sql/recycle_platform_dict_menu_seed.sql:字典和菜单权限种子 SQL 示例。

重要假设

  1. 平台为封闭平台,不开放公众注册。
  2. 平台统一管理资金、数据和反向开票,回收点负责采集真实业务。
  3. 售卖者仅支持自然人个人。
  4. 第一版付款为付款记录登记;现金不扣回收点平台余额,非现金扣减或冻结回收点余额。
  5. 第一版反向开票按一单一票设计。
  6. 身份证图片、付款凭证、现场照片使用附件表统一管理,也可以替换为若依 OSS/文件模块。
  7. 税务政策按当前公开政策整理,最终上线前仍需由客户财税顾问和主管税务机关确认。

需求与字段盘点 01-refined-requirements.md

废品回收平台需求重梳理 V2.0

1. 业务角色

角色来源数据范围主要动作
平台管理员若依 sys_user全平台回收点管理、订单监管、余额监管、充值审核、付款流水、反向开票、财务报表、系统设置
平台财务若依 sys_user全平台资金票据后台加款、线下转账入账、付款复核、冲正、反向开票、红冲作废
平台运营审核员若依 sys_user全平台业务回收点资料、售卖者资料、订单抽查、异常处理
回收点管理员若依 sys_user + 回收点绑定本回收点员工、分类、商品、单位、价格、订单、余额、加款申请
回收点员工若依 sys_user + 回收点绑定本人或本站移动录单、售卖者建档、身份证上传、订单查询、付款登记

2. 核心业务闭环

  1. 平台创建回收点。
  2. 回收点创建员工。
  3. 回收点维护分类、单位、商品和价格。
  4. 员工录入个人售卖者资料,支持身份证 OCR、手工录入和后续补图。
  5. 员工录入回收订单,订单必须关联个人售卖者身份证号。
  6. 系统做额度校验:当月额度、连续 12 个月额度。
  7. 订单提交审核。
  8. 审核通过后进入待付款登记。
  9. 现金付款只登记付款记录,不扣余额;微信/支付宝/银行卡等非现金付款登记扣减或冻结回收点余额。
  10. 付款完成后,资料完整订单进入反向开票池。
  11. 平台按订单逐单反向开票,发票抬头开给平台。
  12. 已开票订单如需作废,走红冲和付款冲正流程。

2.1 反向开票和额度口径

  • 售卖者仅支持自然人个人,系统必须采集身份证号 Hash、脱敏号、正反面图片或待补图状态。
  • 连续 12 个月反向开票累计销售额默认配置为 500 万元,不含增值税,超过后阻断录入或提交审核。
  • 个人当月 10 万元默认作为税费处理提示线,不直接等同于禁止开票;平台可在额度配置中设置为预警或阻断。
  • 2026-07-01 起如接入“三流合一反向开票”,年销售额 60 万元以内部分可按较低个税预缴率配置;第一版未接在线支付时只预留字段。
  • 每笔订单独立生成反向开票记录,一单一票,便于订单、付款、开票三方对账。

3. 字段补充原则

为了方便后续审计、对账、风控和若依代码生成,字段适当冗余:

  • 订单表冗余回收点名称、售卖者姓名脱敏、身份证号 Hash、订单金额、付款状态、开票状态。
  • 订单明细表冗余商品名称、分类名称、单位名称、下单单价,避免商品后续改名改价影响历史订单。
  • 付款记录冗余付款方式、外部流水号、第三方支付流水号、凭证附件、余额影响金额。
  • 反向开票表冗余订单号、回收点名称、售卖者姓名、身份证号 Hash、开票金额、税费金额。
  • 所有业务表保留若依通用审计字段:create_by、create_time、update_by、update_time、remark、del_flag。

4. 回收点字段

字段说明
station_id主键
station_code回收点编号,平台生成
station_name回收点名称
contact_name/contact_phone联系人和手机号
province/city/district/address地址
business_status经营状态:0停用、1启用
account_status余额账户状态:normal/frozen/disabled
manager_user_id默认负责人用户 ID
license_no可选,营业执照或内部备案号
settle_bank_*回收点打款账户信息,后续退款或线下核对使用

5. 员工字段

员工仍使用若依 sys_user 登录体系,业务侧增加回收点用户绑定表。

字段说明
user_id若依用户 ID
station_id所属回收点
employee_no员工编号
station_rolestation_admin/station_staff
data_scopeself/station
id_card_no可选,员工实名管理
status0停用、1启用

6. 售卖者字段

字段说明
seller_id主键
seller_name个人姓名
mobile手机号
id_card_no_encrypt身份证号密文
id_card_no_hash身份证号 Hash,用于去重和额度统计
id_card_mask脱敏身份证号
id_card_front_file_id/back_file_id身份证正反面附件
id_card_image_statuscomplete/pending
ocr_statusnone/success/fail/manual
receive_typecash/wechat/alipay/bank
receive_account收款账号密文或脱敏
bank_name/bank_card_no_encrypt/bank_card_mask银行卡信息
invoice_consent_flag是否同意反向开票
risk_statusnormal/black/limited

7. 分类、单位、商品、价格字段

分类:

  • station_id、category_code、category_name、parent_id、sort_no、status。

单位:

  • station_id、unit_code、unit_name、decimal_scale、status。

商品:

  • station_id、category_id、product_code、product_name、unit_id、default_price、price_status、status、sort_no。

价格记录:

  • product_id、unit_id、old_price、new_price、change_source、order_id、change_reason、change_by、change_time。

8. 订单字段

订单主表:

字段说明
order_id/order_no主键和订单号
station_id/station_name回收点
employee_user_id/employee_name录单员工
seller_id/seller_name/seller_id_card_hash/seller_id_card_mask售卖者快照
order_amount订单总金额
payable_amount应付金额
paid_amount已付金额
invoice_amount拟开票金额
item_count商品行数
order_statusdraft/pending_audit/audit_pass/audit_reject/void
payment_statusnot_required/pending/confirming/paid/cancel/reversed
invoice_statusnot_enter/waiting/applying/invoiced/reding/reded/void
id_card_image_status身份证图片状态快照
quota_check_statuspass/fail/warn
quota_check_msg超额提示
print_count打印次数

订单明细:

  • product_id、product_name、category_id、category_name、unit_id、unit_name、quantity、unit_price、default_price、manual_price_flag、amount。

9. 付款字段

付款记录:

字段说明
payment_id/payment_no主键和付款单号
order_id/order_no订单
station_id/station_name回收点
seller_id/seller_name售卖者
payment_methodcash/wechat/alipay/bank
payment_amount付款金额
balance_affect_flag是否影响余额
balance_flow_id余额流水
external_trade_no外部转账流水或备注
third_trade_no/third_order_no/channel后续在线支付预留
voucher_file_id付款凭证
payment_statuspending/confirmed/failed/reversed
paid_time付款时间

余额账户:

  • station_id、available_balance、frozen_balance、total_recharge、total_pay、total_refund、account_status。

余额流水:

  • flow_no、station_id、biz_type、biz_id、direction、amount、before_balance、after_balance、frozen_amount、operator_user_id。

10. 充值/加款字段

字段说明
recharge_id/recharge_no主键和申请单号
station_id/station_name回收点
recharge_typebackend_add/offline_transfer/wechat/alipay/online_bank
apply_amount/approved_amount申请金额/通过金额
voucher_file_id打款凭证
audit_statuspending/pass/reject/accounted/cancel
audit_user_id/audit_time审核
account_flow_id入账流水

11. 反向开票字段

字段说明
invoice_id/invoice_no主键和内部开票单号
order_id/order_no一单一票
station_id/station_name回收点
seller_id/seller_name/seller_id_card_hash售卖者
buyer_name/buyer_tax_no平台公司抬头
invoice_amount开票金额
vat_amount/additional_tax_amount/personal_tax_amount税费
tax_calc_rulenormal/triple_flow
tax_report_period/tax_report_status代办税费所属期和申报状态
invoice_statuswaiting/applying/invoiced/failed/reding/reded/void
tax_invoice_code/tax_invoice_no税票号码
e_invoice_file_id电子发票
red_invoice_id/red_reason红冲

12. 附件字段

统一附件表记录身份证、付款凭证、现场照片、发票文件:

  • file_id、biz_type、biz_id、file_type、file_name、file_url、storage_type、mime_type、file_size、hash、upload_user_id。

13. 状态字典

详见 05-dict-and-permission.md

数据库设计 02-database-design.md

数据库设计说明

1. 设计约定

  • 数据库:MySQL 8.x,字符集 utf8mb4。
  • 金额字段统一使用 decimal(18,2)
  • 数量字段使用 decimal(18,4)
  • 主键使用 bigint unsigned auto_increment,若项目使用雪花 ID,可改为 bigint 手动赋值。
  • 若依代码生成建议字段:create_by、create_time、update_by、update_time、remark、del_flag。
  • 敏感字段不要明文存储,SQL 中保留 encrypt/hash/mask 三类字段。

2. 表清单

表名中文名说明
rec_station回收点表平台维护回收点
rec_station_user回收点用户绑定表绑定若依 sys_user
rec_seller个人售卖者表身份证、收款信息、授权
rec_category回收分类表回收点自定义分类
rec_unit计量单位表回收点自定义单位
rec_product回收商品表商品、单位、默认价
rec_product_price_log商品价格记录表调价和订单改价留痕
rec_order回收订单主表订单头、金额、状态
rec_order_item回收订单明细表商品明细快照
rec_order_audit_log订单审核记录表审核通过/驳回
rec_order_change_log订单修改记录表修改、作废、红冲关联
rec_payment_record付款记录表现金/非现金付款登记
rec_balance_account回收点余额账户表可用、冻结、累计
rec_balance_flow余额流水表加款、扣款、冻结、冲正
rec_recharge_apply加款申请表线下转账、后台加款
rec_reverse_invoice反向开票表一单一票和红冲
rec_quota_config额度配置表月提示线、12个月硬限制、三流合一个税优惠线
rec_quota_usage_log额度占用记录表订单额度计算留痕
rec_attachment附件表身份证、凭证、照片、发票
rec_print_log打印记录表订单打印留痕

3. 核心关系

  • rec_station 1:N rec_station_user。
  • rec_station 1:N rec_category / rec_unit / rec_product。
  • rec_seller 1:N rec_order。
  • rec_order 1:N rec_order_item。
  • rec_order 1:N rec_payment_record,但正常业务建议 1:1,冲正/补付时可多条。
  • rec_station 1:1 rec_balance_account。
  • rec_balance_account 1:N rec_balance_flow。
  • rec_order 1:1 rec_reverse_invoice,一单一票。
  • rec_attachment 通过 biz_type + biz_id 关联任意业务。

4. 冗余字段说明

订单、付款、开票表存在 station_name、seller_name、order_no 等冗余字段,是为了:

  1. 历史快照不受基础资料修改影响。
  2. 列表查询减少联表。
  3. 导出和审计更稳定。
  4. 对接第三方支付、票务接口时减少上下文丢失。

5. 索引建议

  • 身份证统计使用 id_card_no_hash,不要用密文字段。
  • 订单列表常用索引:station_id、seller_id、order_no、order_status、payment_status、invoice_status、order_time。
  • 财务流水常用索引:station_id、biz_type、biz_id、flow_time。
  • 开票池常用索引:invoice_status、order_id、seller_id。

6. 额度和税费配置建议

  • PERSON_MONTH_TAX_FREE_AMOUNT:个人当月销售额 10 万元提示线,默认预警,是否阻断由平台配置。
  • PERSON_ROLLING_12_REVERSE_LIMIT:个人连续 12 个月反向开票累计 500 万元硬限制,默认阻断。
  • PERSON_YEAR_TRIPLE_FLOW_TAX_BREAK:三流合一反向开票年度 60 万元个税优惠分段,第一版预留,接入线上支付后启用。
  • 额度判断以身份证 Hash 维度汇总,统计范围排除已作废、已红冲且业务撤销的订单。

接口定义 03-api-design.md

接口定义

1. 统一约定

  • 前缀:/recycle
  • 返回:若依 AjaxResult / TableDataInfo。
  • 分页:pageNumpageSize
  • 导出:若依 ExcelUtil。
  • 权限:@PreAuthorize("@ss.hasPermi('recycle:xxx:yyy')")
  • 金额字段:字符串或 decimal,前端展示保留 2 位。
  • 附件上传:可先复用若依通用上传接口,也可提供 /recycle/attachment/upload

2. 回收点管理

方法地址说明权限
GET/recycle/station/list回收点分页recycle:station:list
GET/recycle/station/{stationId}详情recycle:station:query
POST/recycle/station新增recycle:station:add
PUT/recycle/station修改recycle:station:edit
DELETE/recycle/station/{stationIds}删除/停用recycle:station:remove
PUT/recycle/station/{stationId}/status启用停用recycle:station:edit
POST/recycle/station/{stationId}/initAccount初始化余额账户recycle:station:account

新增参数示例:

{
  "stationCode": "CD001",
  "stationName": "城东再生资源站",
  "contactName": "周经理",
  "contactPhone": "13900000000",
  "province": "浙江省",
  "city": "杭州市",
  "district": "上城区",
  "address": "工业路18号",
  "managerUserId": 101
}

3. 回收点员工

方法地址说明权限
GET/recycle/stationUser/list员工绑定列表recycle:stationUser:list
POST/recycle/stationUser绑定/新增员工recycle:stationUser:add
PUT/recycle/stationUser修改站点角色recycle:stationUser:edit
PUT/recycle/stationUser/{id}/status启停用recycle:stationUser:edit

4. 售卖者

方法地址说明权限
GET/recycle/seller/list售卖者列表recycle:seller:list
GET/recycle/seller/{sellerId}售卖者详情recycle:seller:query
GET/recycle/seller/idCard/{hash}按身份证Hash查询recycle:seller:query
POST/recycle/seller新增售卖者recycle:seller:add
PUT/recycle/seller修改售卖者recycle:seller:edit
POST/recycle/seller/ocr身份证OCR识别recycle:seller:ocr
POST/recycle/seller/{sellerId}/idCardImage补传身份证图片recycle:seller:edit

5. 分类、单位、商品、价格

方法地址说明权限
GET/recycle/category/list分类列表recycle:category:list
POST/recycle/category新增分类recycle:category:add
PUT/recycle/category修改分类recycle:category:edit
GET/recycle/unit/list单位列表recycle:unit:list
POST/recycle/unit新增单位recycle:unit:add
PUT/recycle/unit修改单位recycle:unit:edit
GET/recycle/product/list商品列表recycle:product:list
POST/recycle/product新增商品recycle:product:add
PUT/recycle/product修改商品recycle:product:edit
PUT/recycle/product/changePrice商品调价recycle:product:price
GET/recycle/product/{productId}/priceTrend价格走势图recycle:product:query

调价参数:

{
  "productId": 1,
  "newPrice": 1.25,
  "changeReason": "市场价调整"
}

6. 回收订单

方法地址说明权限
GET/recycle/order/list订单列表recycle:order:list
GET/recycle/order/{orderId}订单详情recycle:order:query
POST/recycle/order创建订单recycle:order:add
PUT/recycle/order修改未开票订单recycle:order:edit
POST/recycle/order/{orderId}/submit提交审核recycle:order:submit
POST/recycle/order/{orderId}/audit审核通过/驳回recycle:order:audit
POST/recycle/order/{orderId}/void作废订单recycle:order:void
GET/recycle/order/{orderId}/print打印订单数据recycle:order:print
GET/recycle/order/export导出订单recycle:order:export

创建订单参数:

{
  "stationId": 1,
  "sellerId": 10,
  "items": [
    {
      "productId": 100,
      "quantity": 128.4,
      "unitPrice": 1.25,
      "manualPriceFlag": "0"
    },
    {
      "productId": 101,
      "quantity": 18,
      "unitPrice": 7.20,
      "manualPriceFlag": "1",
      "changeReason": "现场议价"
    }
  ],
  "sceneFileIds": [9001],
  "remark": "现场回收"
}

审核参数:

{
  "auditAction": "pass",
  "auditRemark": "资料完整,金额无误"
}

7. 付款记录

方法地址说明权限
GET/recycle/payment/list付款记录列表recycle:payment:list
GET/recycle/payment/{paymentId}详情recycle:payment:query
POST/recycle/payment/register登记付款recycle:payment:add
POST/recycle/payment/{paymentId}/confirm确认付款recycle:payment:confirm
POST/recycle/payment/{paymentId}/reverse付款冲正recycle:payment:reverse
GET/recycle/payment/export导出付款记录recycle:payment:export

登记付款参数:

{
  "orderId": 10001,
  "paymentMethod": "bank",
  "paymentAmount": 620.50,
  "externalTradeNo": "BANK202608060001",
  "voucherFileId": 9011,
  "paidTime": "2026-08-06 10:20:00",
  "remark": "银行卡转账登记"
}

业务规则:

  • cash:不生成余额扣减流水。
  • wechat/alipay/bank:校验回收点可用余额,余额不足阻断。
  • confirmed 后订单 payment_status 变为 paid。

8. 余额账户和加款

方法地址说明权限
GET/recycle/account/list平台查看余额账户recycle:account:list
GET/recycle/account/{stationId}回收点余额详情recycle:account:query
GET/recycle/account/flow/list余额流水recycle:account:flow
POST/recycle/recharge/apply回收点发起加款recycle:recharge:add
GET/recycle/recharge/list加款申请列表recycle:recharge:list
POST/recycle/recharge/{rechargeId}/audit平台审核入账recycle:recharge:audit
POST/recycle/account/backendAdd后台加款recycle:account:add
POST/recycle/account/adjust人工调账recycle:account:adjust

9. 反向开票

方法地址说明权限
GET/recycle/invoice/pool待开票池recycle:invoice:list
GET/recycle/invoice/list开票记录recycle:invoice:list
POST/recycle/invoice/createByOrder/{orderId}按订单生成开票申请recycle:invoice:add
POST/recycle/invoice/{invoiceId}/confirm录入发票号码/文件recycle:invoice:confirm
POST/recycle/invoice/{invoiceId}/fail开票失败recycle:invoice:edit
POST/recycle/invoice/{invoiceId}/red发票红冲recycle:invoice:red
GET/recycle/invoice/export导出开票数据recycle:invoice:export

10. 额度配置

方法地址说明权限
GET/recycle/quota/list额度配置recycle:quota:list
PUT/recycle/quota修改额度recycle:quota:edit
GET/recycle/quota/check按身份证和金额试算额度recycle:quota:query

11. 附件

方法地址说明权限
POST/recycle/attachment/upload上传附件recycle:attachment:add
GET/recycle/attachment/list附件列表recycle:attachment:list
GET/recycle/attachment/{fileId}查看/下载recycle:attachment:query

若依开发落地 04-ruoyi-development-guide.md

若依 Vue3 开发落地说明

1. 模块建议

RuoYi-Vue3 后端建议新增业务模块:

ruoyi-recycle
  └── com.ruoyi.recycle
      ├── controller
      ├── domain
      ├── mapper
      ├── service
      └── service.impl

也可以直接放在 ruoyi-admin 中,但不建议,后续业务会变重。前端按 RuoYi-Vue3 的 src/viewssrc/apisrc/routersrc/components 组织。

2. 包和类命名

业务ControllerDomainServiceMapper
回收点RecStationControllerRecStationIRecStationServiceRecStationMapper
员工绑定RecStationUserControllerRecStationUserIRecStationUserServiceRecStationUserMapper
售卖者RecSellerControllerRecSellerIRecSellerServiceRecSellerMapper
分类RecCategoryControllerRecCategoryIRecCategoryServiceRecCategoryMapper
单位RecUnitControllerRecUnitIRecUnitServiceRecUnitMapper
商品RecProductControllerRecProductIRecProductServiceRecProductMapper
订单RecOrderControllerRecOrderIRecOrderServiceRecOrderMapper
付款RecPaymentControllerRecPaymentRecordIRecPaymentServiceRecPaymentRecordMapper
余额RecBalanceControllerRecBalanceAccountIRecBalanceServiceRecBalanceAccountMapper
加款RecRechargeControllerRecRechargeApplyIRecRechargeServiceRecRechargeApplyMapper
开票RecInvoiceControllerRecReverseInvoiceIRecInvoiceServiceRecReverseInvoiceMapper
额度RecQuotaControllerRecQuotaConfigIRecQuotaServiceRecQuotaConfigMapper

3. 若依代码生成建议

适合直接代码生成的单表:

  • rec_station
  • rec_station_user
  • rec_seller
  • rec_category
  • rec_unit
  • rec_product
  • rec_product_price_log
  • rec_recharge_apply
  • rec_reverse_invoice
  • rec_quota_config

不建议完全依赖代码生成,需要手写业务逻辑:

  • rec_order + rec_order_item:主子表、金额计算、额度校验。
  • rec_payment_record + rec_balance_flow:事务扣减余额。
  • rec_balance_account:并发更新余额。
  • rec_reverse_invoice:开票前置校验和红冲。

4. 事务边界

必须使用 @Transactional(rollbackFor = Exception.class) 的场景:

  1. 创建订单:订单主表、明细表、价格改价记录、额度占用记录。
  2. 订单审核:订单状态、审核记录。
  3. 登记非现金付款:付款记录、余额扣减/冻结、余额流水、订单付款状态。
  4. 付款冲正:付款记录、余额流水、订单付款状态。
  5. 加款审核入账:加款申请、余额账户、余额流水。
  6. 开票确认:开票记录、订单开票状态。
  7. 红冲作废:开票记录、订单状态、付款冲正、变更日志。

5. 余额并发控制

非现金付款扣减余额时,建议 SQL 条件更新:

UPDATE rec_balance_account
SET available_balance = available_balance - #{amount},
    total_pay = total_pay + #{amount},
    update_time = NOW()
WHERE station_id = #{stationId}
  AND available_balance >= #{amount}
  AND account_status = 'normal';

返回影响行数为 0 时,提示余额不足或账户不可用。

6. 身份证和银行卡安全

  • 明文只在请求入参短暂存在,不落库。
  • 入库保存 encrypt、hash、mask 三份。
  • hash 用于唯一性和额度统计。
  • mask 用于列表展示。
  • encrypt 用于确需还原的场景,需后端权限控制。
  • 身份证图片下载必须记录操作日志。

7. 订单金额计算

后端必须重新计算:

  • 明细小计 = quantity * unit_price。
  • 订单总额 = 明细小计汇总。
  • 前端传入金额只作展示,不作为最终可信值。

8. 额度和税费校验建议

提交审核前校验:

  • 当月累计:按 id_card_no_hash + order_time 当月 + 未作废订单,默认只提示税费处理风险。
  • 连续 12 个月累计:按 id_card_no_hash + 最近 12 个月 + 未作废订单,默认超过 500 万元阻断。
  • 三流合一年累计:按 id_card_no_hash + 自然年 + 已完成三流合一付款订单,第一版预留。
  • 是否阻断读取 rec_quota_config.control_action/block_flag,不写死在代码里。

9. 权限和数据范围

平台角色:

  • 查看所有 station_id。

回收点管理员:

  • station_id = 当前绑定回收点。

回收点员工:

  • 默认 employee_user_id = 当前登录用户。
  • 若配置 data_scope=station,可查看本站全部订单。

实现方式:

  • 在 Service 查询条件中注入 stationId/userId。
  • 不建议只依赖前端隐藏菜单。

10. Vue3 前端页面建议

平台端:

  • 回收点管理
  • 订单监管
  • 余额账户
  • 加款审核
  • 付款记录
  • 反向开票
  • 财务报表
  • 系统设置/额度设置

回收点 PC:

  • 工作台
  • 员工管理
  • 分类管理
  • 商品管理
  • 单位管理
  • 价格管理
  • 订单查询
  • 余额中心
  • 加款申请

移动端 H5/uni-app:

  • 首页
  • 录入订单
  • 订单列表
  • 站点信息
  • 我的明细
  • 分类信息
  • 商品信息

Vue3 页面、API 封装、权限按钮和字典使用详见 06-vue3-frontend-guide.md

11. 开发优先级建议

第一阶段后端:

  1. 回收点、员工绑定、分类、单位、商品、价格。
  2. 售卖者、附件、身份证资料。
  3. 订单主流程。
  4. 余额账户、加款、付款登记。
  5. 反向开票手工记录。
  6. 报表和导出。

第二阶段:

  • 在线支付。
  • OCR 服务商。
  • 电子发票服务商。
  • 自动对账。
  • 风控黑名单。

字典和权限 05-dict-and-permission.md

字典和权限设计

1. 字典类型

字典类型字典名称
rec_business_status经营状态1启用、0停用
rec_account_status余额账户状态normal正常、frozen冻结、disabled禁用
rec_station_role回收点角色station_admin回收点管理员、station_staff员工
rec_data_scope回收点数据范围self本人、station本站
rec_id_card_image_status身份证图片状态pending待补全、complete完整
rec_ocr_statusOCR状态none未识别、success成功、fail失败、manual人工录入
rec_receive_type收款方式cash现金、wechat微信、alipay支付宝、bank银行卡
rec_order_status订单状态draft草稿、pending_audit待审核、audit_pass审核通过、audit_reject审核驳回、void已作废
rec_payment_status付款状态not_required未到付款、pending待登记、confirming待确认、paid已支付、cancel已取消、reversed已冲正
rec_payment_method付款方式cash现金、wechat微信、alipay支付宝、bank银行卡
rec_invoice_status开票状态not_enter未进入、waiting待开票、applying申请中、invoiced已开票、failed失败、reding红冲中、reded已红冲、void已作废
rec_recharge_type加款方式backend_add后台加款、offline_transfer线下转账、wechat微信充值、alipay支付宝充值、online_bank网银
rec_recharge_status加款状态pending待审核、pass已通过、reject已驳回、accounted已入账、cancel已取消
rec_balance_biz_type余额流水类型recharge充值、pay付款、freeze冻结、unfreeze解冻、reverse冲正、refund退款、adjust调账
rec_quota_period额度周期month当月、rolling_12_month连续12个月、year年度

2. 菜单权限

平台端:

  • recycle:station:list/query/add/edit/remove/account
  • recycle:order:list/query/audit/void/print/export
  • recycle:account:list/query/flow/add/adjust
  • recycle:recharge:list/query/audit/export
  • recycle:payment:list/query/add/confirm/reverse/export
  • recycle:invoice:list/query/add/confirm/red/export
  • recycle:quota:list/query/edit

回收点端:

  • recycle:stationUser:list/add/edit
  • recycle:category:list/add/edit/remove
  • recycle:unit:list/add/edit/remove
  • recycle:product:list/add/edit/remove/price
  • recycle:order:list/query/add/edit/submit/print/export
  • recycle:recharge:add/list/query
  • recycle:account:query/flow

移动端:

  • recycle:mobile:home
  • recycle:mobile:order:add/list/query
  • recycle:mobile:seller:add/query/ocr
  • recycle:mobile:station:query
  • recycle:mobile:product:list

Vue3 前端说明 06-vue3-frontend-guide.md

RuoYi-Vue3 前端开发说明

1. 技术栈约定

  • Vue 3
  • Vite
  • Element Plus
  • 若依 Vue3 风格的 src/api/** 接口封装
  • 若依权限指令:v-hasPermi
  • 列表页沿用 queryParamsgetListhandleQueryresetQueryhandleAddhandleUpdatehandleDeletehandleExport 的写法

2. 前端目录建议

src/
  api/
    recycle/
      station.js
      stationUser.js
      seller.js
      category.js
      unit.js
      product.js
      order.js
      payment.js
      account.js
      recharge.js
      invoice.js
      quota.js
      attachment.js
  views/
    recycle/
      platform/
        station/index.vue
        order/index.vue
        account/index.vue
        recharge/index.vue
        payment/index.vue
        invoice/index.vue
        report/index.vue
        quota/index.vue
      station/
        dashboard/index.vue
        staff/index.vue
        category/index.vue
        unit/index.vue
        product/index.vue
        price/index.vue
        order/index.vue
        balance/index.vue
        recharge/index.vue
      components/
        SellerSelect.vue
        ProductItemEditor.vue
        PaymentRegisterDialog.vue
        OrderPrintDialog.vue
        AttachmentUpload.vue
        PriceTrend.vue

3. API 封装示例

// src/api/recycle/order.js
import request from '@/utils/request'

export function listOrder(query) {
  return request({
    url: '/recycle/order/list',
    method: 'get',
    params: query
  })
}

export function getOrder(orderId) {
  return request({
    url: '/recycle/order/' + orderId,
    method: 'get'
  })
}

export function addOrder(data) {
  return request({
    url: '/recycle/order',
    method: 'post',
    data
  })
}

export function updateOrder(data) {
  return request({
    url: '/recycle/order',
    method: 'put',
    data
  })
}

export function submitOrder(orderId) {
  return request({
    url: '/recycle/order/' + orderId + '/submit',
    method: 'post'
  })
}

export function auditOrder(orderId, data) {
  return request({
    url: '/recycle/order/' + orderId + '/audit',
    method: 'post',
    data
  })
}

4. 列表页 Vue3 写法示例

<script setup>
import { ref, reactive, toRefs, getCurrentInstance } from 'vue'
import { listOrder } from '@/api/recycle/order'

const { proxy } = getCurrentInstance()
const loading = ref(false)
const total = ref(0)
const orderList = ref([])

const data = reactive({
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    orderNo: undefined,
    stationId: undefined,
    orderStatus: undefined,
    paymentStatus: undefined,
    invoiceStatus: undefined
  }
})

const { queryParams } = toRefs(data)

function getList() {
  loading.value = true
  listOrder(queryParams.value).then(res => {
    orderList.value = res.rows
    total.value = res.total
    loading.value = false
  })
}

function handleQuery() {
  queryParams.value.pageNum = 1
  getList()
}

function resetQuery() {
  proxy.resetForm('queryRef')
  handleQuery()
}

getList()
</script>

5. 页面设计建议

平台管理后台

页面主要组件说明
回收点管理el-table + el-dialog管理回收点、初始化余额账户
订单监管el-table + el-drawer全平台订单查询、审核、打印
余额账户el-table + el-descriptions回收点余额、流水、后台加款
充值审核el-table + audit dialog线下转账审核入账
付款记录el-table + voucher preview现金/非现金付款登记和冲正
反向开票el-table + el-drawer待开票池、一单一票、红冲
财务报表el-card + charts金额、付款、开票统计
额度设置el-form月额度、12个月额度、阻断开关

回收点 PC 后台

页面主要组件说明
工作台指标卡 + 表格本站订单、余额、商品概览
员工管理el-table员工绑定和启停用
分类管理el-table/tree本站分类
商品管理el-table商品、单位、默认价
单位管理el-table公斤、斤、吨、个等
价格管理PriceTrend调价记录和趋势图
订单查询el-table + drawer本站订单筛选、打印
余额中心el-descriptions + el-table余额和流水
加款申请el-form + upload线下转账加款

6. 字典使用

Vue3 页面使用若依字典 hook:

const { rec_order_status, rec_payment_status, rec_invoice_status } = proxy.useDict(
  'rec_order_status',
  'rec_payment_status',
  'rec_invoice_status'
)

表格显示:

<dict-tag :options="rec_order_status" :value="scope.row.orderStatus" />

7. 权限按钮

<el-button
  type="primary"
  plain
  icon="Plus"
  v-hasPermi="['recycle:order:add']"
  @click="handleAdd"
>
  新增
</el-button>

8. 移动端说明

员工端准备用 uni-app,不建议塞进 RuoYi-Vue3 后台前端。建议作为独立 H5/小程序项目,共用后端接口,登录可对接若依 Token 或单独移动端登录接口。

税务与额度 07-tax-policy-and-quota.md

反向开票政策口径和额度配置

1. 政策口径摘要

本系统不是税务判断工具,开发上只把政策口径做成可配置参数、校验提示和留痕。上线前需要由客户财税顾问按企业所在地区和主管税务机关要求确认。

控制项建议系统配置默认动作说明
个人当月销售额提示线10万元预警用于提示增值税、附加税和代办税费处理,不建议直接当作“禁止开票”硬规则
个人连续12个月反向开票上限500万元阻断超过后自然人不再符合资源回收企业反向开票条件
三流合一个税优惠年度额度60万元税费分段2026-07-01 后接入三流合一反向开票时启用,第一版预留

2. 官方来源

  • 国家税务总局公告2024年第5号:关于资源回收企业向自然人报废产品出售者“反向开票”有关事项的公告,https://fgk.chinatax.gov.cn/zcfgk/c100012/c5223305/content.html
  • 国家税务总局公告2026年第12号:关于“三流合一反向开票”个人所得税有关征管事项的公告,https://fgk.chinatax.gov.cn/zcfgk/c100012/c5250556/content.html
  • 财政部、税务总局关于小规模纳税人增值税优惠政策的现行口径:月销售额10万元以下增值税优惠。

3. 系统额度配置

建议使用 rec_quota_config 做统一配置,不在代码里写死金额。

quota_code用途周期金额control_action
PERSON_MONTH_TAX_FREE_AMOUNT当月销售额提示线month100000.00warn
PERSON_ROLLING_12_REVERSE_LIMIT连续12个月反向开票上限rolling_12_month5000000.00block
PERSON_YEAR_TRIPLE_FLOW_TAX_BREAK三流合一个税优惠年度额度year600000.00tax_calc

4. 订单校验时点

时点校验内容失败处理
创建草稿售卖者身份证是否存在、商品单价和金额计算草稿可保存,但提示资料待补
提交审核身份证 Hash、额度、订单金额、明细完整性超过 block 额度则不允许提交
审核通过再次校验订单金额和售卖者状态不通过则驳回
登记付款非现金校验回收点余额余额不足不允许登记非现金付款
生成开票付款完成、未作废、未开票、授权和身份证信息完整不满足则不进入开票池

5. 税费字段预留

反向开票表保留:

  • vat_amount:增值税金额。
  • additional_tax_amount:附加税费。
  • personal_tax_amount:个人所得税。
  • tax_calc_rule:normal/triple_flow。
  • tax_report_period:代办税费所属期。
  • tax_report_status:代办税费申报状态。
  • tax_report_no:代办税费报告编号。

6. 开发注意

  • 额度统计按身份证 Hash,不按 seller_id,因为同一自然人可能在多个回收点发生交易。
  • 统计金额建议以已审核或已支付订单为准,具体口径由平台配置。
  • 已作废订单不计入额度;已红冲订单是否扣减额度需按财务最终处理状态确定。
  • 额度日志 rec_quota_usage_log 需要记录 before_amount、order_amount、after_amount、quota_amount 和 check_status,便于客户解释为什么被阻断。

后端代码骨架 08-backend-code-skeleton.md

若依后端代码骨架

1. 开发包结构

ruoyi-recycle
  src/main/java/com/ruoyi/recycle
    controller
    domain
    mapper
    service
    service/impl
    dto
    vo
    enums
    util
  src/main/resources/mapper/recycle

如果先不拆模块,也可以放在 ruoyi-admin 下,但包名仍建议保持 com.ruoyi.recycle,后续迁移成本更低。

2. 订单 Controller 示例

@RestController
@RequestMapping("/recycle/order")
public class RecOrderController extends BaseController {
    @Autowired
    private IRecOrderService recOrderService;

    @PreAuthorize("@ss.hasPermi('recycle:order:list')")
    @GetMapping("/list")
    public TableDataInfo list(RecOrderQuery query) {
        startPage();
        return getDataTable(recOrderService.selectRecOrderList(query));
    }

    @PreAuthorize("@ss.hasPermi('recycle:order:add')")
    @PostMapping
    public AjaxResult add(@RequestBody RecOrderCreateDto dto) {
        return success(recOrderService.createOrder(dto));
    }

    @PreAuthorize("@ss.hasPermi('recycle:order:submit')")
    @PostMapping("/{orderId}/submit")
    public AjaxResult submit(@PathVariable Long orderId) {
        recOrderService.submitOrder(orderId);
        return success();
    }

    @PreAuthorize("@ss.hasPermi('recycle:order:audit')")
    @PostMapping("/{orderId}/audit")
    public AjaxResult audit(@PathVariable Long orderId, @RequestBody RecOrderAuditDto dto) {
        recOrderService.auditOrder(orderId, dto);
        return success();
    }
}

3. 订单 Service 事务方法

@Transactional(rollbackFor = Exception.class)
public Long createOrder(RecOrderCreateDto dto) {
    RecSeller seller = sellerService.selectById(dto.getSellerId());
    Assert.notNull(seller, "售卖者不存在");

    RecOrder order = buildOrderSnapshot(dto, seller);
    recOrderMapper.insertRecOrder(order);

    for (RecOrderItemDto itemDto : dto.getItems()) {
        RecOrderItem item = buildItemSnapshot(order, itemDto);
        recOrderItemMapper.insertRecOrderItem(item);
        if ("1".equals(item.getManualPriceFlag())) {
            priceLogService.recordOrderPriceChange(order, item, itemDto.getChangeReason());
        }
    }

    recOrderMapper.updateOrderAmount(order.getOrderId(), recalcOrderAmount(order.getOrderId()));
    quotaService.checkAndRecord(order.getOrderId());
    return order.getOrderId();
}

4. 余额扣减 Mapper SQL

<update id="deductAvailableBalance">
  UPDATE rec_balance_account
  SET available_balance = available_balance - #{amount},
      total_pay = total_pay + #{amount},
      update_time = NOW()
  WHERE station_id = #{stationId}
    AND available_balance >= #{amount}
    AND account_status = 'normal'
</update>

Service 中判断影响行数:

int rows = recBalanceAccountMapper.deductAvailableBalance(stationId, amount);
if (rows == 0) {
    throw new ServiceException("回收点余额不足或账户不可用");
}

5. 付款登记流程

1. 校验订单状态 = audit_pass。
2. 校验付款金额 = 订单应付金额或允许补付金额。
3. cash:只写 rec_payment_record,不写余额扣减流水。
4. wechat/alipay/bank:先扣余额,再写 rec_balance_flow,再写 rec_payment_record。
5. 更新订单 paid_amount/payment_status。
6. paid 后可进入反向开票池。

6. 额度校验伪代码

public QuotaCheckResult checkAndRecord(Long orderId) {
    RecOrder order = orderMapper.selectRecOrderByOrderId(orderId);
    List<RecQuotaConfig> configs = quotaMapper.selectEnabledConfigs();
    for (RecQuotaConfig config : configs) {
        BigDecimal beforeAmount = quotaMapper.sumSellerAmount(
            order.getSellerIdCardHash(),
            config.getPeriodType(),
            order.getOrderTime()
        );
        BigDecimal afterAmount = beforeAmount.add(order.getPayableAmount());
        String status = resolveQuotaStatus(config, afterAmount);
        quotaUsageLogMapper.insert(buildLog(order, config, beforeAmount, afterAmount, status));
        if ("block".equals(config.getControlAction()) && "fail".equals(status)) {
            throw new ServiceException(config.getQuotaName() + "已超限");
        }
    }
    return QuotaCheckResult.pass();
}

7. DTO 建议

DTO字段
RecOrderCreateDtostationId、sellerId、items、sceneFileIds、remark
RecOrderItemDtoproductId、quantity、unitPrice、manualPriceFlag、changeReason
RecOrderAuditDtoauditAction、auditRemark
RecPaymentRegisterDtoorderId、paymentMethod、paymentAmount、externalTradeNo、voucherFileId、paidTime、remark
RecRechargeAuditDtoauditAction、approvedAmount、auditRemark
RecInvoiceConfirmDtoinvoiceId、taxInvoiceCode、taxInvoiceNo、invoiceDate、eInvoiceFileId、taxAmount fields

8. 开发任务拆分

模块后端任务前端 Vue3 任务
基础资料回收点、员工绑定、分类、单位、商品、价格日志平台回收点、回收点资料维护页面
售卖者身份证 Hash/脱敏/加密、OCR 接口适配、附件售卖者选择、身份证上传、补图
订单主子表、审核、修改、作废、打印订单录入、订单列表、详情抽屉、打印
财务余额账户、余额流水、加款审核、付款登记余额中心、充值审核、付款流水
开票待开票池、一单一票、红冲字段反向开票列表、开票确认、红冲
报表订单/付款/开票统计接口财务报表图表和导出

9. 必做测试

  • 同一身份证跨回收点额度累计。
  • 现金付款不扣余额。
  • 非现金余额不足阻断。
  • 手动改价写入价格记录。
  • 未开票订单允许修改,已开票订单必须走作废/红冲。
  • 一单只生成一条有效反向开票记录。
  • 回收点员工只能看到本人或本站数据,不能越权查看其他回收点。

业务表 SQL sql/recycle_platform_schema.sql

CREATE DATABASE IF NOT EXISTS recycle_platform DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE recycle_platform;

DROP TABLE IF EXISTS rec_station;
CREATE TABLE rec_station (
  station_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '回收点ID',
  station_code VARCHAR(32) NOT NULL COMMENT '回收点编号',
  station_name VARCHAR(100) NOT NULL COMMENT '回收点名称',
  contact_name VARCHAR(50) DEFAULT NULL COMMENT '联系人',
  contact_phone VARCHAR(30) DEFAULT NULL COMMENT '联系电话',
  province VARCHAR(50) DEFAULT NULL COMMENT '省',
  city VARCHAR(50) DEFAULT NULL COMMENT '市',
  district VARCHAR(50) DEFAULT NULL COMMENT '区县',
  address VARCHAR(255) DEFAULT NULL COMMENT '详细地址',
  longitude DECIMAL(12,6) DEFAULT NULL COMMENT '经度',
  latitude DECIMAL(12,6) DEFAULT NULL COMMENT '纬度',
  manager_user_id BIGINT DEFAULT NULL COMMENT '默认负责人用户ID',
  license_no VARCHAR(80) DEFAULT NULL COMMENT '备案/营业执照号',
  settle_bank_name VARCHAR(100) DEFAULT NULL COMMENT '结算开户行',
  settle_bank_account_mask VARCHAR(80) DEFAULT NULL COMMENT '结算账号脱敏',
  business_status CHAR(1) NOT NULL DEFAULT '1' COMMENT '经营状态 1启用 0停用',
  account_status VARCHAR(20) NOT NULL DEFAULT 'normal' COMMENT '余额账户状态 normal/frozen/disabled',
  create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
  del_flag CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志 0正常 2删除',
  PRIMARY KEY (station_id),
  UNIQUE KEY uk_station_code (station_code),
  KEY idx_station_name (station_name)
) ENGINE=InnoDB COMMENT='回收点表';

DROP TABLE IF EXISTS rec_station_user;
CREATE TABLE rec_station_user (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
  station_id BIGINT UNSIGNED NOT NULL COMMENT '回收点ID',
  user_id BIGINT NOT NULL COMMENT '若依用户ID',
  employee_no VARCHAR(32) DEFAULT NULL COMMENT '员工编号',
  station_role VARCHAR(30) NOT NULL COMMENT '站点角色 station_admin/station_staff',
  data_scope VARCHAR(20) NOT NULL DEFAULT 'self' COMMENT '数据范围 self/station',
  status CHAR(1) NOT NULL DEFAULT '1' COMMENT '状态 1启用 0停用',
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (id),
  UNIQUE KEY uk_station_user (station_id, user_id),
  KEY idx_user_id (user_id)
) ENGINE=InnoDB COMMENT='回收点用户绑定表';

DROP TABLE IF EXISTS rec_seller;
CREATE TABLE rec_seller (
  seller_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '售卖者ID',
  seller_name VARCHAR(60) NOT NULL COMMENT '姓名',
  mobile VARCHAR(30) DEFAULT NULL COMMENT '手机号',
  id_card_no_encrypt VARCHAR(255) DEFAULT NULL COMMENT '身份证号密文',
  id_card_no_hash CHAR(64) NOT NULL COMMENT '身份证号Hash',
  id_card_mask VARCHAR(32) DEFAULT NULL COMMENT '身份证脱敏',
  id_card_front_file_id BIGINT DEFAULT NULL COMMENT '身份证正面附件ID',
  id_card_back_file_id BIGINT DEFAULT NULL COMMENT '身份证反面附件ID',
  id_card_image_status VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '身份证图片状态 pending/complete',
  ocr_status VARCHAR(20) NOT NULL DEFAULT 'none' COMMENT 'OCR状态 none/success/fail/manual',
  ocr_raw_json TEXT DEFAULT NULL COMMENT 'OCR原始结果',
  receive_type VARCHAR(20) DEFAULT NULL COMMENT '收款方式 cash/wechat/alipay/bank',
  receive_account_encrypt VARCHAR(255) DEFAULT NULL COMMENT '收款账号密文',
  receive_account_mask VARCHAR(80) DEFAULT NULL COMMENT '收款账号脱敏',
  bank_name VARCHAR(100) DEFAULT NULL COMMENT '开户行',
  bank_card_no_encrypt VARCHAR(255) DEFAULT NULL COMMENT '银行卡密文',
  bank_card_mask VARCHAR(80) DEFAULT NULL COMMENT '银行卡脱敏',
  invoice_consent_flag CHAR(1) NOT NULL DEFAULT '0' COMMENT '是否同意反向开票',
  consent_file_id BIGINT DEFAULT NULL COMMENT '授权/同意材料附件',
  risk_status VARCHAR(20) NOT NULL DEFAULT 'normal' COMMENT '风险状态 normal/black/limited',
  first_order_time DATETIME DEFAULT NULL COMMENT '首次交易时间',
  last_order_time DATETIME DEFAULT NULL COMMENT '最近交易时间',
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (seller_id),
  UNIQUE KEY uk_id_card_hash (id_card_no_hash),
  KEY idx_mobile (mobile),
  KEY idx_seller_name (seller_name)
) ENGINE=InnoDB COMMENT='个人售卖者表';

DROP TABLE IF EXISTS rec_category;
CREATE TABLE rec_category (
  category_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  station_id BIGINT UNSIGNED NOT NULL,
  parent_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
  category_code VARCHAR(32) DEFAULT NULL,
  category_name VARCHAR(80) NOT NULL,
  sort_no INT NOT NULL DEFAULT 0,
  status CHAR(1) NOT NULL DEFAULT '1',
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (category_id),
  KEY idx_station_category (station_id, category_name)
) ENGINE=InnoDB COMMENT='回收分类表';

DROP TABLE IF EXISTS rec_unit;
CREATE TABLE rec_unit (
  unit_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  station_id BIGINT UNSIGNED NOT NULL,
  unit_code VARCHAR(32) DEFAULT NULL,
  unit_name VARCHAR(40) NOT NULL,
  decimal_scale TINYINT NOT NULL DEFAULT 2 COMMENT '小数位',
  status CHAR(1) NOT NULL DEFAULT '1',
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (unit_id),
  KEY idx_station_unit (station_id, unit_name)
) ENGINE=InnoDB COMMENT='计量单位表';

DROP TABLE IF EXISTS rec_product;
CREATE TABLE rec_product (
  product_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  station_id BIGINT UNSIGNED NOT NULL,
  category_id BIGINT UNSIGNED NOT NULL,
  unit_id BIGINT UNSIGNED NOT NULL,
  product_code VARCHAR(32) DEFAULT NULL,
  product_name VARCHAR(100) NOT NULL,
  default_price DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '默认单价',
  price_status VARCHAR(20) NOT NULL DEFAULT 'normal' COMMENT '价格状态 normal/changed',
  sort_no INT NOT NULL DEFAULT 0,
  status CHAR(1) NOT NULL DEFAULT '1',
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (product_id),
  KEY idx_station_product (station_id, product_name),
  KEY idx_category_id (category_id)
) ENGINE=InnoDB COMMENT='回收商品表';

DROP TABLE IF EXISTS rec_product_price_log;
CREATE TABLE rec_product_price_log (
  price_log_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  station_id BIGINT UNSIGNED NOT NULL,
  product_id BIGINT UNSIGNED NOT NULL,
  unit_id BIGINT UNSIGNED DEFAULT NULL,
  old_price DECIMAL(18,4) DEFAULT NULL,
  new_price DECIMAL(18,4) NOT NULL,
  change_source VARCHAR(30) NOT NULL COMMENT 'backend/order',
  order_id BIGINT UNSIGNED DEFAULT NULL,
  change_reason VARCHAR(255) DEFAULT NULL,
  change_user_id BIGINT DEFAULT NULL,
  change_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  PRIMARY KEY (price_log_id),
  KEY idx_product_time (product_id, change_time),
  KEY idx_station_time (station_id, change_time)
) ENGINE=InnoDB COMMENT='商品价格记录表';

DROP TABLE IF EXISTS rec_order;
CREATE TABLE rec_order (
  order_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  order_no VARCHAR(40) NOT NULL,
  station_id BIGINT UNSIGNED NOT NULL,
  station_name VARCHAR(100) NOT NULL,
  employee_user_id BIGINT DEFAULT NULL,
  employee_name VARCHAR(60) DEFAULT NULL,
  seller_id BIGINT UNSIGNED NOT NULL,
  seller_name VARCHAR(60) NOT NULL,
  seller_mobile VARCHAR(30) DEFAULT NULL,
  seller_id_card_hash CHAR(64) NOT NULL,
  seller_id_card_mask VARCHAR(32) DEFAULT NULL,
  order_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  item_count INT NOT NULL DEFAULT 0,
  order_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  payable_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  paid_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  invoice_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  order_status VARCHAR(30) NOT NULL DEFAULT 'draft',
  payment_status VARCHAR(30) NOT NULL DEFAULT 'not_required',
  invoice_status VARCHAR(30) NOT NULL DEFAULT 'not_enter',
  id_card_image_status VARCHAR(20) NOT NULL DEFAULT 'pending',
  quota_check_status VARCHAR(20) NOT NULL DEFAULT 'pass',
  quota_check_msg VARCHAR(500) DEFAULT NULL,
  audit_user_id BIGINT DEFAULT NULL,
  audit_time DATETIME DEFAULT NULL,
  audit_remark VARCHAR(500) DEFAULT NULL,
  void_flag CHAR(1) NOT NULL DEFAULT '0',
  void_time DATETIME DEFAULT NULL,
  print_count INT NOT NULL DEFAULT 0,
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (order_id),
  UNIQUE KEY uk_order_no (order_no),
  KEY idx_station_time (station_id, order_time),
  KEY idx_seller_time (seller_id, order_time),
  KEY idx_status (order_status, payment_status, invoice_status),
  KEY idx_id_card_hash (seller_id_card_hash)
) ENGINE=InnoDB COMMENT='回收订单主表';

DROP TABLE IF EXISTS rec_order_item;
CREATE TABLE rec_order_item (
  item_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  order_id BIGINT UNSIGNED NOT NULL,
  order_no VARCHAR(40) NOT NULL,
  station_id BIGINT UNSIGNED NOT NULL,
  category_id BIGINT UNSIGNED DEFAULT NULL,
  category_name VARCHAR(80) DEFAULT NULL,
  product_id BIGINT UNSIGNED DEFAULT NULL,
  product_name VARCHAR(100) NOT NULL,
  unit_id BIGINT UNSIGNED DEFAULT NULL,
  unit_name VARCHAR(40) NOT NULL,
  quantity DECIMAL(18,4) NOT NULL DEFAULT 0,
  default_price DECIMAL(18,4) DEFAULT NULL,
  unit_price DECIMAL(18,4) NOT NULL DEFAULT 0,
  manual_price_flag CHAR(1) NOT NULL DEFAULT '0',
  amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  sort_no INT NOT NULL DEFAULT 0,
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  PRIMARY KEY (item_id),
  KEY idx_order_id (order_id),
  KEY idx_product_id (product_id)
) ENGINE=InnoDB COMMENT='回收订单明细表';

DROP TABLE IF EXISTS rec_order_audit_log;
CREATE TABLE rec_order_audit_log (
  audit_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  order_id BIGINT UNSIGNED NOT NULL,
  order_no VARCHAR(40) NOT NULL,
  audit_action VARCHAR(30) NOT NULL COMMENT 'submit/pass/reject/void',
  before_status VARCHAR(30) DEFAULT NULL,
  after_status VARCHAR(30) DEFAULT NULL,
  audit_user_id BIGINT DEFAULT NULL,
  audit_user_name VARCHAR(60) DEFAULT NULL,
  audit_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  audit_remark VARCHAR(500) DEFAULT NULL,
  PRIMARY KEY (audit_id),
  KEY idx_order_id (order_id)
) ENGINE=InnoDB COMMENT='订单审核记录表';

DROP TABLE IF EXISTS rec_order_change_log;
CREATE TABLE rec_order_change_log (
  change_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  order_id BIGINT UNSIGNED NOT NULL,
  order_no VARCHAR(40) NOT NULL,
  change_type VARCHAR(30) NOT NULL COMMENT 'edit/void/red/reverse',
  before_json TEXT DEFAULT NULL,
  after_json TEXT DEFAULT NULL,
  change_reason VARCHAR(500) DEFAULT NULL,
  change_user_id BIGINT DEFAULT NULL,
  change_user_name VARCHAR(60) DEFAULT NULL,
  change_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (change_id),
  KEY idx_order_id (order_id)
) ENGINE=InnoDB COMMENT='订单修改记录表';

DROP TABLE IF EXISTS rec_balance_account;
CREATE TABLE rec_balance_account (
  account_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  station_id BIGINT UNSIGNED NOT NULL,
  available_balance DECIMAL(18,2) NOT NULL DEFAULT 0,
  frozen_balance DECIMAL(18,2) NOT NULL DEFAULT 0,
  total_recharge DECIMAL(18,2) NOT NULL DEFAULT 0,
  total_pay DECIMAL(18,2) NOT NULL DEFAULT 0,
  total_refund DECIMAL(18,2) NOT NULL DEFAULT 0,
  account_status VARCHAR(20) NOT NULL DEFAULT 'normal',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  PRIMARY KEY (account_id),
  UNIQUE KEY uk_station_id (station_id)
) ENGINE=InnoDB COMMENT='回收点余额账户表';

DROP TABLE IF EXISTS rec_balance_flow;
CREATE TABLE rec_balance_flow (
  flow_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  flow_no VARCHAR(40) NOT NULL,
  station_id BIGINT UNSIGNED NOT NULL,
  account_id BIGINT UNSIGNED NOT NULL,
  biz_type VARCHAR(40) NOT NULL COMMENT 'recharge/pay/freeze/unfreeze/reverse/refund/adjust',
  biz_id BIGINT UNSIGNED DEFAULT NULL,
  direction VARCHAR(10) NOT NULL COMMENT 'in/out/freeze/unfreeze',
  amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  before_balance DECIMAL(18,2) NOT NULL DEFAULT 0,
  after_balance DECIMAL(18,2) NOT NULL DEFAULT 0,
  before_frozen DECIMAL(18,2) NOT NULL DEFAULT 0,
  after_frozen DECIMAL(18,2) NOT NULL DEFAULT 0,
  operator_user_id BIGINT DEFAULT NULL,
  flow_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  PRIMARY KEY (flow_id),
  UNIQUE KEY uk_flow_no (flow_no),
  KEY idx_station_time (station_id, flow_time),
  KEY idx_biz (biz_type, biz_id)
) ENGINE=InnoDB COMMENT='余额流水表';

DROP TABLE IF EXISTS rec_payment_record;
CREATE TABLE rec_payment_record (
  payment_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  payment_no VARCHAR(40) NOT NULL,
  order_id BIGINT UNSIGNED NOT NULL,
  order_no VARCHAR(40) NOT NULL,
  station_id BIGINT UNSIGNED NOT NULL,
  station_name VARCHAR(100) NOT NULL,
  seller_id BIGINT UNSIGNED NOT NULL,
  seller_name VARCHAR(60) NOT NULL,
  payment_method VARCHAR(20) NOT NULL COMMENT 'cash/wechat/alipay/bank',
  payment_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  balance_affect_flag CHAR(1) NOT NULL DEFAULT '0',
  balance_flow_id BIGINT UNSIGNED DEFAULT NULL,
  external_trade_no VARCHAR(100) DEFAULT NULL,
  third_channel VARCHAR(40) DEFAULT NULL,
  third_order_no VARCHAR(100) DEFAULT NULL,
  third_trade_no VARCHAR(100) DEFAULT NULL,
  third_callback_time DATETIME DEFAULT NULL,
  voucher_file_id BIGINT DEFAULT NULL,
  payment_status VARCHAR(30) NOT NULL DEFAULT 'pending',
  paid_time DATETIME DEFAULT NULL,
  reverse_payment_id BIGINT UNSIGNED DEFAULT NULL,
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (payment_id),
  UNIQUE KEY uk_payment_no (payment_no),
  KEY idx_order_id (order_id),
  KEY idx_station_time (station_id, create_time),
  KEY idx_status (payment_status)
) ENGINE=InnoDB COMMENT='付款记录表';

DROP TABLE IF EXISTS rec_recharge_apply;
CREATE TABLE rec_recharge_apply (
  recharge_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  recharge_no VARCHAR(40) NOT NULL,
  station_id BIGINT UNSIGNED NOT NULL,
  station_name VARCHAR(100) NOT NULL,
  recharge_type VARCHAR(30) NOT NULL COMMENT 'backend_add/offline_transfer/wechat/alipay/online_bank',
  apply_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  approved_amount DECIMAL(18,2) DEFAULT NULL,
  voucher_file_id BIGINT DEFAULT NULL,
  apply_user_id BIGINT DEFAULT NULL,
  apply_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  audit_status VARCHAR(30) NOT NULL DEFAULT 'pending',
  audit_user_id BIGINT DEFAULT NULL,
  audit_time DATETIME DEFAULT NULL,
  audit_remark VARCHAR(500) DEFAULT NULL,
  account_flow_id BIGINT UNSIGNED DEFAULT NULL,
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (recharge_id),
  UNIQUE KEY uk_recharge_no (recharge_no),
  KEY idx_station_time (station_id, apply_time),
  KEY idx_status (audit_status)
) ENGINE=InnoDB COMMENT='加款申请表';

DROP TABLE IF EXISTS rec_reverse_invoice;
CREATE TABLE rec_reverse_invoice (
  invoice_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  invoice_apply_no VARCHAR(40) NOT NULL COMMENT '内部开票申请号',
  order_id BIGINT UNSIGNED NOT NULL,
  order_no VARCHAR(40) NOT NULL,
  station_id BIGINT UNSIGNED NOT NULL,
  station_name VARCHAR(100) NOT NULL,
  seller_id BIGINT UNSIGNED NOT NULL,
  seller_name VARCHAR(60) NOT NULL,
  seller_id_card_hash CHAR(64) NOT NULL,
  buyer_name VARCHAR(150) NOT NULL COMMENT '平台公司抬头',
  buyer_tax_no VARCHAR(50) DEFAULT NULL COMMENT '平台税号',
  invoice_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  vat_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  additional_tax_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  personal_tax_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  tax_calc_rule VARCHAR(30) DEFAULT NULL COMMENT 'normal/triple_flow',
  tax_report_period VARCHAR(20) DEFAULT NULL COMMENT '代办税费所属期 yyyy-MM',
  tax_report_status VARCHAR(30) NOT NULL DEFAULT 'not_reported' COMMENT 'not_reported/reported',
  tax_report_no VARCHAR(80) DEFAULT NULL COMMENT '代办税费报告编号',
  invoice_status VARCHAR(30) NOT NULL DEFAULT 'waiting',
  tax_invoice_code VARCHAR(80) DEFAULT NULL,
  tax_invoice_no VARCHAR(80) DEFAULT NULL,
  invoice_date DATE DEFAULT NULL,
  e_invoice_file_id BIGINT DEFAULT NULL,
  fail_reason VARCHAR(500) DEFAULT NULL,
  red_invoice_id BIGINT UNSIGNED DEFAULT NULL,
  red_reason VARCHAR(500) DEFAULT NULL,
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (invoice_id),
  UNIQUE KEY uk_invoice_apply_no (invoice_apply_no),
  UNIQUE KEY uk_order_id (order_id),
  KEY idx_seller (seller_id_card_hash),
  KEY idx_status (invoice_status)
) ENGINE=InnoDB COMMENT='反向开票表';

DROP TABLE IF EXISTS rec_quota_config;
CREATE TABLE rec_quota_config (
  quota_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  quota_code VARCHAR(40) NOT NULL,
  quota_name VARCHAR(100) NOT NULL,
  quota_type VARCHAR(30) NOT NULL DEFAULT 'amount' COMMENT 'amount/tax/invoice',
  period_type VARCHAR(30) NOT NULL COMMENT 'month/rolling_12_month/year',
  quota_amount DECIMAL(18,2) NOT NULL,
  control_action VARCHAR(20) NOT NULL DEFAULT 'warn' COMMENT 'warn/block/tax_calc',
  block_flag CHAR(1) NOT NULL DEFAULT '1',
  warning_ratio DECIMAL(6,4) DEFAULT 0.8,
  effective_start DATE DEFAULT NULL,
  effective_end DATE DEFAULT NULL,
  policy_basis VARCHAR(255) DEFAULT NULL,
  status CHAR(1) NOT NULL DEFAULT '1',
  create_by VARCHAR(64) DEFAULT '',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_by VARCHAR(64) DEFAULT '',
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  remark VARCHAR(500) DEFAULT NULL,
  PRIMARY KEY (quota_id),
  UNIQUE KEY uk_quota_code (quota_code)
) ENGINE=InnoDB COMMENT='额度配置表';

DROP TABLE IF EXISTS rec_quota_usage_log;
CREATE TABLE rec_quota_usage_log (
  usage_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  seller_id BIGINT UNSIGNED NOT NULL,
  id_card_no_hash CHAR(64) NOT NULL,
  order_id BIGINT UNSIGNED NOT NULL,
  order_no VARCHAR(40) NOT NULL,
  quota_code VARCHAR(40) NOT NULL,
  before_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  order_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  after_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  quota_amount DECIMAL(18,2) NOT NULL DEFAULT 0,
  check_status VARCHAR(20) NOT NULL COMMENT 'pass/warn/fail',
  check_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (usage_id),
  KEY idx_seller_quota (id_card_no_hash, quota_code, check_time),
  KEY idx_order_id (order_id)
) ENGINE=InnoDB COMMENT='额度占用记录表';

DROP TABLE IF EXISTS rec_attachment;
CREATE TABLE rec_attachment (
  file_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  biz_type VARCHAR(40) NOT NULL,
  biz_id BIGINT UNSIGNED NOT NULL,
  file_type VARCHAR(40) NOT NULL COMMENT 'id_front/id_back/payment_voucher/order_scene/invoice/consent',
  file_name VARCHAR(255) NOT NULL,
  file_url VARCHAR(500) NOT NULL,
  storage_type VARCHAR(30) NOT NULL DEFAULT 'local',
  mime_type VARCHAR(80) DEFAULT NULL,
  file_size BIGINT DEFAULT NULL,
  file_hash CHAR(64) DEFAULT NULL,
  upload_user_id BIGINT DEFAULT NULL,
  upload_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  del_flag CHAR(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (file_id),
  KEY idx_biz (biz_type, biz_id),
  KEY idx_file_type (file_type)
) ENGINE=InnoDB COMMENT='业务附件表';

DROP TABLE IF EXISTS rec_print_log;
CREATE TABLE rec_print_log (
  print_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  order_id BIGINT UNSIGNED NOT NULL,
  order_no VARCHAR(40) NOT NULL,
  station_id BIGINT UNSIGNED NOT NULL,
  print_type VARCHAR(30) NOT NULL DEFAULT 'order',
  print_user_id BIGINT DEFAULT NULL,
  print_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  print_snapshot TEXT DEFAULT NULL,
  PRIMARY KEY (print_id),
  KEY idx_order_id (order_id),
  KEY idx_station_time (station_id, print_time)
) ENGINE=InnoDB COMMENT='打印记录表';

INSERT INTO rec_quota_config (quota_code, quota_name, quota_type, period_type, quota_amount, control_action, block_flag, warning_ratio, effective_start, policy_basis, remark)
VALUES
('PERSON_MONTH_TAX_FREE_AMOUNT', '个人当月销售额提示线', 'tax', 'month', 100000.00, 'warn', '0', 0.8, '2023-01-01', '小规模纳税人月销售额10万元以下增值税优惠口径', '默认10万元,建议预警,不建议直接按政策硬阻断'),
('PERSON_ROLLING_12_REVERSE_LIMIT', '个人连续12个月反向开票上限', 'invoice', 'rolling_12_month', 5000000.00, 'block', '1', 0.8, '2024-04-29', '国家税务总局公告2024年第5号', '默认500万元,超过后自然人不再符合反向开票条件'),
('PERSON_YEAR_TRIPLE_FLOW_TAX_BREAK', '三流合一个税优惠年度额度', 'tax', 'year', 600000.00, 'tax_calc', '0', 0.8, '2026-07-01', '国家税务总局公告2026年第12号', '接入三流合一反向开票后启用,第一版预留');

字典菜单 SQL sql/recycle_platform_dict_menu_seed.sql

-- 字典和菜单种子示例:请按实际 sys_dict_type/sys_dict_data/sys_menu 主键策略调整

INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
SELECT '回收订单状态', 'rec_order_status', '0', 'admin', NOW(), '废品回收平台'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'rec_order_status');

INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
VALUES
(1, '草稿', 'draft', 'rec_order_status', NULL, 'default', 'Y', '0', 'admin', NOW(), NULL),
(2, '待审核', 'pending_audit', 'rec_order_status', NULL, 'warning', 'N', '0', 'admin', NOW(), NULL),
(3, '审核通过', 'audit_pass', 'rec_order_status', NULL, 'success', 'N', '0', 'admin', NOW(), NULL),
(4, '审核驳回', 'audit_reject', 'rec_order_status', NULL, 'danger', 'N', '0', 'admin', NOW(), NULL),
(5, '已作废', 'void', 'rec_order_status', NULL, 'info', 'N', '0', 'admin', NOW(), NULL);

INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
SELECT '回收付款方式', 'rec_payment_method', '0', 'admin', NOW(), '废品回收平台'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'rec_payment_method');

INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
VALUES
(1, '现金', 'cash', 'rec_payment_method', NULL, 'info', 'Y', '0', 'admin', NOW(), NULL),
(2, '微信', 'wechat', 'rec_payment_method', NULL, 'success', 'N', '0', 'admin', NOW(), NULL),
(3, '支付宝', 'alipay', 'rec_payment_method', NULL, 'primary', 'N', '0', 'admin', NOW(), NULL),
(4, '银行卡', 'bank', 'rec_payment_method', NULL, 'warning', 'N', '0', 'admin', NOW(), NULL);

-- 菜单示例,只提供权限标识清单,实际 menu_id/parent_id 建议在若依后台或迁移脚本中确认
-- 平台端:recycle:station:list, recycle:order:list, recycle:account:list, recycle:recharge:list, recycle:payment:list, recycle:invoice:list, recycle:quota:list
-- 回收点端:recycle:category:list, recycle:unit:list, recycle:product:list, recycle:order:list, recycle:account:query, recycle:recharge:list