|
|
bdac4ab7b2
|
fix(fin): 激活配置并发行锁保护 FIN-03
- commission_withdrawal_setting/service.go: 激活事务内首步加 FOR UPDATE 行锁,防止并发时出现多条 is_active=true
- wechat_config_store.go: ActivateInTx 激活前同样加行锁保护
|
2026-03-28 10:38:53 +08:00 |
|
|
|
0ebb3d75f3
|
fix(realname): C 端实名检查改为按卡类型判断 REALNAME-03
- 删除 packagesNeedRealname 函数(依赖已废弃的 EnableRealnameActivation)
- 改为按 assetInfo.CardCategory==normal 判断是否需要实名
- 行业卡(industry)绕过实名检查,普通卡(normal)需要实名
|
2026-03-28 10:38:44 +08:00 |
|
|
|
7dd55630dc
|
fix(realname): activateMainPackage 三维决策重构 REALNAME-02
- 替换 EnableRealnameActivation 布尔判断为三维决策逻辑
- 行业卡(card_category=industry)永远直接激活
- C 端购买(BuyerTypePersonal)前置实名检查已完成,直接激活
- 后台囤货按 ExpiryBase 决定:from_activation 等实名,from_purchase 立即激活
|
2026-03-28 10:38:24 +08:00 |
|
|
|
9ee2f0b466
|
fix(fin): 提现拒绝补 remark 必填校验 FIN-02
- CommissionWithdrawalHandler 新增 validator 字段
- RejectWithdrawal 在 BodyParser 后补充 validator.Struct 参数验证,确保 remark 必填
|
2026-03-28 10:37:54 +08:00 |
|
|
|
244a2742c1
|
fix(realname): DTO 更新 expiry_base 替换 enable_realname_activation REALNAME-05
- package_dto.go: CreatePackageRequest/UpdatePackageRequest/PackageResponse 三处替换
- service.go: 同步更新 DTO 映射逻辑使用 ExpiryBase
|
2026-03-28 10:37:51 +08:00 |
|
|
|
8f62496250
|
fix(fin): 补全提现审批人字段 FIN-01
- Approve updates map 补充 approved_by 和 approved_at 字段,确保审批人信息正确填充
|
2026-03-28 10:36:33 +08:00 |
|
|
|
344850fe1d
|
fix(realname): DB 迁移 + Model + Store 清理 REALNAME-01
- 新增迁移 000089: 移除 enable_realname_activation,添加 expiry_base 字段
- package.go: 删除 EnableRealnameActivation 字段,新增 ExpiryBase 字段
- package_store.go: 移除两步写入特殊处理,改为直接一步 Create
|
2026-03-28 10:36:31 +08:00 |
|
|
|
2a92ab64af
|
fix(perm): 修复企业账号资产权限边界 PERM-01/02
- PERM-01: 删除 Resolve 接口对企业账号的错误拦截(企业账号应可查询资产)
- PERM-02: 在 Refresh 接口开头新增企业账号拦截(企业账号只读,不允许主动触发运营商刷新)
|
2026-03-28 10:36:17 +08:00 |
|
|
|
62eb43a1cf
|
docs(02-perm-fin-realname-poll): 创建 Phase 2 执行计划(2 个 Plan)
|
2026-03-28 10:29:50 +08:00 |
|
|
|
84aca2fd63
|
docs(02): capture phase context
|
2026-03-28 10:11:04 +08:00 |
|
|
|
c083596ef6
|
docs(phase-01): evolve PROJECT.md after phase completion — move CRITICAL-01~08 to Validated
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m18s
|
2026-03-28 09:49:15 +08:00 |
|
|
|
fb14c51fc2
|
docs(phase-01): complete phase execution — verification passed, all CRITICAL-01~08 satisfied
|
2026-03-28 09:47:54 +08:00 |
|
|
|
dbeed92ed9
|
docs(01-03): 完成充值回调自动购包链路修复 Plan 执行总结
- 创建 01-03-SUMMARY.md(CRITICAL-04 + CRITICAL-05 修复记录)
- 更新 STATE.md(Plan 计数推进至 4/5,progress 100%)
- 更新 ROADMAP.md(Phase 1 进度更新)
- 标记 CRITICAL-04、CRITICAL-05 需求已完成
|
2026-03-27 23:33:47 +08:00 |
|
|
|
030425be33
|
feat(01-03): AutoPurchaseHandler 注入 asynqClient,触发佣金并注册处理器(CRITICAL-04/05)
- AutoPurchaseHandler struct 新增 asynqClient *asynq.Client 字段
- NewAutoPurchaseHandler() 新增 asynqClient 参数
- ProcessTask 事务成功后入队 TaskTypeCommission(事务外,防止一致性问题)
- pkg/queue/handler.go 新增 registerAutoPurchaseHandler() 方法
- RegisterHandlers() 调用 registerAutoPurchaseHandler()
|
2026-03-27 23:31:49 +08:00 |
|
|
|
0ed804c4b3
|
docs(01-02): 完成 PollingHandler 注入 asynq.Client 修复断链计划摘要
|
2026-03-27 23:30:47 +08:00 |
|
|
|
20b4b6d6bf
|
fix(01-02): 修复实名激活任务断链,为 PollingHandler 注入 asynq.Client
- PollingHandler struct 新增 asynqClient *asynq.Client 字段
- NewPollingHandler 构造函数新增 asynqClient 参数(redis 之后,gatewayClient 之前)
- triggerFirstRealnameActivation 改用 asynqClient.EnqueueContext,删除 RPush 降级方案
- 删除 _ = task 废弃代码
- registerPollingHandlers 传入 h.asynqClient 参数
|
2026-03-27 23:29:12 +08:00 |
|
|
|
9a4d87a0d4
|
feat(01-03): 充值回调触发自动购包任务入队(CRITICAL-04)
- recharge.Service struct 新增 queueClient *queue.Client 字段
- recharge.New() 新增 queueClient 参数
- HandlePaymentCallback 事务成功后,LinkedPackageIDs 非空时入队 TaskTypeAutoPurchaseAfterRecharge
- bootstrap/services.go 的 recharge.New() 传入 deps.QueueClient
|
2026-03-27 23:29:05 +08:00 |
|
|
|
0df992c98a
|
docs(01-04): 完成 sellerCostPrice CRITICAL-06 修正计划摘要
|
2026-03-27 23:25:48 +08:00 |
|
|
|
f74b7da25f
|
fix(01-04): 修正场景5代理代购下级的sellerCostPrice赋值
- 三处代理代购下级分支(wallet场景子场景2.2)均将 sellerCostPrice = buyerCostPrice 改为 sellerCostPrice = operatorCostPrice
- buyerCostPrice 是下级成本价,operatorCostPrice 是操作方(代理自己)的成本价
- 使用 buyerCostPrice 会导致佣金差额为 0,所有上级代理无法获得应有佣金
- 修复涉及三个函数(普通下单/后台下单/H5下单),行号分别约在 264/551/813
|
2026-03-27 23:24:16 +08:00 |
|
|
|
6de830fcc4
|
docs(01-01): 完成实名状态常量统一计划执行总结
- 创建 01-01-SUMMARY.md(CRITICAL-01 + CRITICAL-02 完成)
- STATE.md: 更新进度至 Plan 3/5,记录执行指标
- ROADMAP.md: 更新 Phase 1 计划进度(2/5 完成)
- REQUIREMENTS.md: 标记 CRITICAL-01、CRITICAL-02 已完成
|
2026-03-27 22:58:05 +08:00 |
|
|
|
da50a35a62
|
fix(01-01): 修复 C 端实名校验使用常量(CRITICAL-02)
- client_order/service.go: 实名校验 assetInfo.RealNameStatus != 1 改为常量比较
- client_realname.go: 实名状态检查 == 1 改为 constants.RealNameStatusVerified
- go build ./... 编译通过,无遗留硬编码 != 1 / == 1 的实名判断
|
2026-03-27 22:54:50 +08:00 |
|
|
|
1fd0072f69
|
fix(01-01): 统一实名状态常量(CRITICAL-01 核心)
- polling_handler.go: parseRealnameStatus 不再返回硬编码 2,改为 constants.RealNameStatusVerified(=1)
- polling_handler.go: isFirstRealname 判断改用常量比较,不再使用 == 2
- polling_handler.go: HandleProtectConsistencyCheck 注释更正为 real_name_status=1
- iot_card.go: RealNameStatus gorm comment 统一为 0-未实名 1-已实名(去除行业卡说明)
- asset_dto.go: RealNameStatus description 更正为 0未实名 1已实名(去除错误的 1实名中 2已实名)
- scheduler.go: getCardCondition 改用常量替代硬编码 0/1/2 判断
- iot_card/service.go: parseGatewayRealnameStatus 注释更正,返回值改为常量
|
2026-03-27 22:53:30 +08:00 |
|
|
|
bfcea1c18f
|
docs(01-05): 完成 Plan 05 执行摘要,更新 STATE/ROADMAP/REQUIREMENTS
- 创建 01-05-SUMMARY.md(CRITICAL-07 + CRITICAL-08 修复记录)
- STATE.md 进度推进至 plan 2/5(20%)
- ROADMAP.md 更新 Phase 1 进度(1/5 SUMMARY 已完成)
- REQUIREMENTS.md 标记 CRITICAL-07、CRITICAL-08 为完成
|
2026-03-27 22:52:27 +08:00 |
|
|
|
db166807c7
|
fix(01-05): 提现冻结并发校验缺失(CRITICAL-08)
- 冻结余额改为 result := tx...Updates(),分离结果对象
- 检查 result.Error 处理数据库错误
- 检查 result.RowsAffected == 0 防止并发余额不足时仍创建提现单
- RowsAffected == 0 时返回 errors.New(CodeInsufficientBalance, ...)
|
2026-03-27 22:50:18 +08:00 |
|
|
|
809cb2b8a8
|
fix(01-05): 设备导入补充 IMEI 字段(CRITICAL-07)
- DeviceRow struct 新增 IMEI string 字段
- buildDeviceColumnIndex 支持 imei/设备imei/imei号 列名映射
- ParseDeviceExcel 解析数据行时填充 row.IMEI
- device_import.go processBatch 创建 model.Device 时赋值 IMEI: row.IMEI
|
2026-03-27 22:49:40 +08:00 |
|
|
|
f48f11beb4
|
docs(01-p0): 创建 Phase 1 P0 紧急修复执行计划(5 个 Plan,涵盖 CRITICAL-01~08)
|
2026-03-27 22:35:28 +08:00 |
|
|
|
5862018e7c
|
docs(01): capture phase context
|
2026-03-27 21:52:09 +08:00 |
|
|
|
dc9bf0c9b7
|
docs: create roadmap (6 phases)
|
2026-03-27 20:18:06 +08:00 |
|
|
|
7f2e978288
|
docs: define v1 requirements
|
2026-03-27 19:07:58 +08:00 |
|
|
|
3078bc71c8
|
docs: add research findings
|
2026-03-27 19:04:17 +08:00 |
|
|
|
cf59380784
|
docs: initialize project
|
2026-03-27 18:50:18 +08:00 |
|
|
|
876151ec1c
|
修复
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m22s
|
2026-03-23 09:49:30 +08:00 |
|
|
|
c10b70757f
|
fix: 资产信息接口 device_realtime 字段返回固定假数据,避免前端因 nil 报错
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m58s
Gateway 同步接口尚未对接,临时为设备类型资产返回 mock 数据,
后续对接后搜索 buildMockDeviceRealtime 替换为真实数据
|
2026-03-21 14:42:48 +08:00 |
|
|
|
4d1e714366
|
fix: 补齐迁移 000076 遗漏的列名重命名(card_wallet_id → asset_wallet_id)
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m52s
迁移 000076 只将表名从 card_wallet 改为 asset_wallet,但遗漏了表内
card_wallet_id 列的重命名,导致 Model 中 column:asset_wallet_id 与数据库
实际列名不匹配,所有涉及该字段的 INSERT/SELECT 均报错 2002。
影响范围:
- tb_asset_recharge_record.card_wallet_id → asset_wallet_id
- tb_asset_wallet_transaction.card_wallet_id → asset_wallet_id
|
2026-03-21 14:30:29 +08:00 |
|
|
|
d2b765327c
|
完整的字段返回
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m52s
|
2026-03-21 13:41:44 +08:00 |
|
|
|
7dfcf41b41
|
fix: 修复卡类型资产绑定键错误导致归属校验永远失败
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m48s
resolveAssetBindingKey 对卡类型错误地返回 card.ICCID 作为绑定键,
但归属校验 isCustomerOwnAsset 使用 card.VirtualNo 比对,二者不一致
导致所有卡资产的 C 端接口返回 403 无权限。
修复:卡类型绑定键改为 card.VirtualNo,与设计文档一致。
附带数据迁移修正已有的错误绑定记录。
|
2026-03-21 11:33:57 +08:00 |
|
|
|
ed334b946b
|
refactor: 清理重构遗留的死代码
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- personal_customer.Service: 删除已迁移到 client_auth 的死方法
(GetProfile/SendVerificationCode/VerifyCode),移除多余的
verificationService/jwtManager 依赖
- 删除 internal/service/customer/ 整个目录(零引用的早期残留)
|
2026-03-21 11:33:06 +08:00 |
|
|
|
95b2334658
|
feat: 资产套餐历史接口新增 package_type 和 status 筛选条件
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m10s
GET /api/c/v1/asset/package-history 支持可选参数:
- package_type: formal(正式套餐) / addon(加油包)
- status: 0(待生效) / 1(生效中) / 2(已用完) / 3(已过期) / 4(已失效)
不传则返回全部,保持向后兼容。
|
2026-03-21 11:01:21 +08:00 |
|
|
|
da66e673fe
|
feat: 接入短信服务,修复 SMS 客户端 API 路径
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
- cmd/api/main.go: 新增 initSMS() 初始化短信客户端并注入 verificationService
- pkg/sms/client.go: 修复 API 路径缺少 /sms 前缀(/api/... → /sms/api/...)
- docker-compose.prod.yml: 添加线上短信服务环境变量
|
2026-03-21 10:51:43 +08:00 |
|
|
|
284f6c15c7
|
fix: 修复个人客户设备绑定查询使用已废弃的 device_no 列名
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m4s
数据库列已重命名为 virtual_no,但 Store 层 3 处原始 SQL 仍使用旧列名 device_no,
导致小程序登录时查询客户资产绑定关系报 column device_no does not exist。
|
2026-03-20 18:20:24 +08:00 |
|
|
|
55918a0b88
|
fix: 修复 C 端公开路由被认证中间件拦截的问题
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m51s
Fiber 的 Group.Use() 在路由表中注册全局 USE 处理器,不区分 Group 对象。
原代码先调用 authProtectedGroup.Use() 再注册公开路由,导致 verify-asset、
wechat-login、miniapp-login、send-code 四个无需认证的接口被拦截返回 1004。
修复方式:公开路由直接注册在 router 上且在任何 Use() 之前,
利用 Fiber 按注册顺序匹配的机制确保公开路由优先命中。
|
2026-03-20 18:01:12 +08:00 |
|
|
|
d2494798aa
|
fix: 修正停复机接口错误码,网关失败不再返回模糊的内部服务器错误
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m13s
- 单卡停复机:网关错误从 CodeInternalError(2001) 改为 CodeGatewayError(1110),前端可看到具体失败原因
- 单卡停复机:DB 更新裸返 GORM error 改为 CodeDatabaseError(2002) 包装
- 设备复机:全部卡失败时错误码从 CodeInternalError 改为 CodeGatewayError
|
2026-03-19 18:37:03 +08:00 |
|
|
|
b9733c4913
|
fix: 修正零售价架构错误 + 清理旧微信配置 + 归档提案 + 前端接口文档
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m12s
1. 修正 retail_price 架构:
- 删除 batch-pricing 接口的 pricing_target 字段和 retail_price 分支
(上级只能改下级成本价,不能改零售价)
- 新增 PATCH /api/admin/packages/:id/retail-price 接口
(代理自己改自己的零售价,校验 retail_price >= cost_price)
2. 清理旧微信 YAML 配置(已全部迁移到数据库 tb_wechat_config):
- 删除 config.yaml 中 wechat.official_account 配置节
- 删除 NewOfficialAccountApp() 旧工厂函数
- 清理 personal_customer service 中的死代码(旧登录/绑定微信方法)
- 清理 docker-compose.prod.yml 中旧微信环境变量和证书挂载注释
3. 归档四个已完成提案到 openspec/changes/archive/
4. 新增前端接口变更说明文档(docs/前端接口变更说明.md)
5. 修正归档提案和 specs 中关于 pricing_target 的错误描述
|
2026-03-19 17:39:43 +08:00 |
|
|
|
9bd55a1695
|
feat: 实现客户端核心业务接口(client-core-business-api)
新增客户端资产、钱包、订单、实名、设备管理等核心业务 Handler 与 DTO:
- 客户端资产信息查询、套餐列表、套餐历史、资产刷新
- 客户端钱包详情、流水、充值校验、充值订单、充值记录
- 客户端订单创建、列表、详情
- 客户端实名认证链接获取
- 客户端设备卡列表、重启、恢复出厂、WiFi配置、切卡
- 客户端订单服务(含微信/支付宝支付流程)
- 强充自动代购异步任务处理
- 数据库迁移 000084:充值记录增加自动代购状态字段
|
2026-03-19 13:28:04 +08:00 |
|
|
|
e78f5794b9
|
feat: 实现客户端换货系统(client-exchange-system)
新增完整换货生命周期管理:后台发起 → 客户端填收货信息 → 后台发货 → 确认完成(含可选全量迁移) → 旧资产转新再销售
后台接口(7个):
- POST /api/admin/exchanges(发起换货)
- GET /api/admin/exchanges(换货列表)
- GET /api/admin/exchanges/:id(换货详情)
- POST /api/admin/exchanges/:id/ship(发货)
- POST /api/admin/exchanges/:id/complete(确认完成+可选迁移)
- POST /api/admin/exchanges/:id/cancel(取消)
- POST /api/admin/exchanges/:id/renew(旧资产转新)
客户端接口(2个):
- GET /api/c/v1/exchange/pending(查询换货通知)
- POST /api/c/v1/exchange/:id/shipping-info(填写收货信息)
核心能力:
- ExchangeOrder 模型与状态机(1待填写→2待发货→3已发货→4已完成,1/2可取消→5)
- 全量迁移事务(11张表:钱包、套餐、标签、客户绑定等)
- 旧资产转新(generation+1、状态重置、新钱包、历史隔离)
- 旧 CardReplacementRecord 表改名为 legacy,is_replaced 过滤改为查新表
- 数据库迁移:000085 新建 tb_exchange_order,000086 旧表改名
|
2026-03-19 13:26:54 +08:00 |
|
|
|
df76e33105
|
feat: 实现 C 端完整认证系统(client-auth-system)
实现面向个人客户的 7 个认证接口(A1-A7),覆盖资产验证、
微信公众号/小程序登录、手机号绑定/换绑、退出登录完整流程。
主要变更:
- 新增 PersonalCustomerOpenID 模型,支持多 AppID 多 OpenID 管理
- 实现有状态 JWT(JWT + Redis 双重校验),支持服务端主动失效
- 扩展微信 SDK:小程序 Code2Session + 3 个 DB 动态工厂函数
- 实现 A1 资产验证 IP 限流(30/min)和 A4 三层验证码限流
- 新增 7 个错误码(1180-1186)和 6 个 Redis Key 函数
- 注册 /api/c/v1/auth/* 下 7 个端点并更新 OpenAPI 文档
- 数据库迁移 000083:新建 tb_personal_customer_openid 表
|
2026-03-19 11:33:41 +08:00 |
|
|
|
ec86dbf463
|
feat: 客户端接口数据模型基础准备
- 新增资产状态、订单来源、操作人类型、实名链接类型常量
- 8个模型新增字段(asset_status/generation/source/retail_price等)
- 数据库迁移000082:7张表15+字段,含存量retail_price回填
- BUG-1修复:代理零售价渠道隔离,cost_price分配锁定
- BUG-2修复:一次性佣金仅客户端订单触发
- BUG-4修复:充值回调Store操作纳入事务
- 新增资产手动停用接口(PATCH /iot-cards/:id/deactivate、/devices/:id/deactivate)
- Carrier管理新增实名链接配置
- 后台订单generation写时快照
- BatchUpdatePricing支持retail_price调价目标
- 清理全部H5旧接口和个人客户旧登录方法
|
2026-03-19 10:56:50 +08:00 |
|
|
|
817d0d6e04
|
更新openspec
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 46s
|
2026-03-17 14:22:01 +08:00 |
|
|
|
b44363b335
|
fix: 修复新建店铺未初始化代理钱包导致充值订单报错
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m6s
新建店铺时在 shop.Service.Create() 中自动初始化主钱包(main)和分佣钱包(commission),修复充值订单创建时「目标店铺主钱包不存在」错误
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
|
2026-03-17 14:08:26 +08:00 |
|
|
|
3e8f613475
|
fix: 修复 OpenAPI 文档生成器启动 panic,路由缺少 path parameter 定义
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m2s
- 新增 UpdateWechatConfigParams/AgentOfflinePayParams 聚合结构体,嵌入 IDReq 提供 path:id 标签
- 修复 PUT /:id 和 POST /:id/offline-pay 路由的 Input 引用
- 修复 Makefile 构建路径从单文件改为包路径,解决多文件编译问题
- 标记 tasks.md 中 1.2.4 迁移任务为已完成
|
2026-03-17 09:45:51 +08:00 |
|