迁移合作订单完整能力,并统一 AppRoleCode 角色权限与首页菜单展示。

新增 CooperationOrder 模块(双 Tab 列表、获客员绑定、主 Tab 扫码)、订单来源/带客单绑定及配套 API 测试;同步引入 roleCode 权限匹配与相关单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 13:33:00 +08:00
parent d310d26293
commit 1970572edd
66 changed files with 4247 additions and 201 deletions

View File

@ -61,6 +61,11 @@ struct OrderEntity: Decodable, Identifiable, Equatable, Hashable {
let photoTravel: OrderPhotoTravel?
let isNeedEdit: Bool
let isRefined: Int
let canBindReferral: Int
let showOrderSource: Int
let referralOrderNo: String
let referralSalerName: String
let referralOrder: BoundReferralOrder?
enum CodingKeys: String, CodingKey {
case photogUid = "photog_uid"
@ -89,6 +94,11 @@ struct OrderEntity: Decodable, Identifiable, Equatable, Hashable {
case photoTravel = "photo_travel"
case isNeedEdit
case isRefined = "is_refined"
case canBindReferral = "can_bind_referral"
case showOrderSource = "show_order_source"
case referralOrderNo = "referral_order_no"
case referralSalerName = "referral_saler_name"
case referralOrder = "referral_order"
}
///
@ -120,6 +130,11 @@ struct OrderEntity: Decodable, Identifiable, Equatable, Hashable {
photoTravel = try container.decodeIfPresent(OrderPhotoTravel.self, forKey: .photoTravel)
isNeedEdit = try container.decodeLossyBool(forKey: .isNeedEdit) ?? true
isRefined = try container.decodeLossyInt(forKey: .isRefined) ?? 0
canBindReferral = try container.decodeLossyInt(forKey: .canBindReferral) ?? 0
showOrderSource = try container.decodeLossyInt(forKey: .showOrderSource) ?? 0
referralOrderNo = try container.decodeLossyString(forKey: .referralOrderNo)
referralSalerName = try container.decodeLossyString(forKey: .referralSalerName)
referralOrder = try container.decodeIfPresent(BoundReferralOrder.self, forKey: .referralOrder)
}
}

View File

@ -0,0 +1,106 @@
import Foundation
struct OrderSourcePerson: Identifiable, Hashable {
let key: String
let name: String
let phone: String
var id: String { key }
}
struct OrderSourceLead: Identifiable, Hashable {
let key: String
let userPhone: String
let remark: String
let images: [String]
var id: String { key }
}
struct OrderSourceSelection: Hashable {
let person: OrderSourcePerson
let lead: OrderSourceLead
}
extension CooperativeSaler {
func toOrderSourcePerson() -> OrderSourcePerson {
OrderSourcePerson(
key: "\(displayId)",
name: displayName,
phone: displayPhone
)
}
}
extension ReferralOrder {
func toOrderSourceLead() -> OrderSourceLead {
let remarkText: String
if !remark.isEmpty {
remarkText = remark
} else if !statusLabel.isEmpty {
remarkText = statusLabel
} else {
remarkText = "带客单 \(displayReferralOrderNo)"
}
return OrderSourceLead(
key: displayReferralOrderNo,
userPhone: userMobile,
remark: remarkText,
images: images
)
}
}
extension BoundReferralOrder {
func toOrderSourceSelection(fallbackSalerName: String = "") -> OrderSourceSelection? {
let referralNo = orderNo.trimmingCharacters(in: .whitespacesAndNewlines)
guard !referralNo.isEmpty else { return nil }
let personName = salerName.isEmpty
? (fallbackSalerName.isEmpty ? "" : fallbackSalerName)
: salerName
let remarkText: String
if !remark.isEmpty {
remarkText = remark
} else if !displayText.isEmpty {
remarkText = displayText
} else {
remarkText = "带客单号 \(referralNo)"
}
return OrderSourceSelection(
person: OrderSourcePerson(
key: saleUserId > 0 ? "\(saleUserId)" : "",
name: personName,
phone: salerPhone
),
lead: OrderSourceLead(
key: referralNo,
userPhone: userMobile,
remark: remarkText,
images: []
)
)
}
}
extension OrderEntity {
func toBoundOrderSourceSelection() -> OrderSourceSelection? {
if let referralOrder,
let selection = referralOrder.toOrderSourceSelection(fallbackSalerName: referralSalerName) {
return selection
}
guard !referralOrderNo.isEmpty else { return nil }
return OrderSourceSelection(
person: OrderSourcePerson(
key: "",
name: referralSalerName.isEmpty ? "" : referralSalerName,
phone: ""
),
lead: OrderSourceLead(
key: referralOrderNo,
userPhone: phone,
remark: "带客单号 \(referralOrderNo)",
images: []
)
)
}
}