同步应用品牌并修复 referral_order 订单列表解析
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_1024.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
@ -12,6 +13,7 @@
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "icon_1024.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
@ -23,6 +25,7 @@
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"filename" : "icon_1024.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
|
||||
BIN
suixinkan/Assets.xcassets/AppIcon.appiconset/icon_1024.png
Normal file
BIN
suixinkan/Assets.xcassets/AppIcon.appiconset/icon_1024.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
21
suixinkan/Assets.xcassets/SplashLogo.imageset/Contents.json
vendored
Normal file
21
suixinkan/Assets.xcassets/SplashLogo.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_splash.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
suixinkan/Assets.xcassets/SplashLogo.imageset/icon_splash.png
vendored
Normal file
BIN
suixinkan/Assets.xcassets/SplashLogo.imageset/icon_splash.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@ -1,7 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24765" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24743"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@ -11,10 +13,52 @@
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" layoutMarginsRelativeArrangement="YES" axis="vertical" alignment="center" spacing="0" translatesAutoresizingMaskIntoConstraints="NO" id="Stk-Mn-001">
|
||||
<rect key="frame" x="0.0" y="59" width="393" height="759"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" verticalHuggingPriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="Spc-Tp-001">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="200.5"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="SplashLogo" translatesAutoresizingMaskIntoConstraints="NO" id="Img-Lg-001">
|
||||
<rect key="frame" x="106.5" y="200.5" width="180" height="180"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="180" id="Img-Wd-001"/>
|
||||
<constraint firstAttribute="height" constant="180" id="Img-Ht-001"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="随心瞰" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Lbl-Tt-001">
|
||||
<rect key="frame" x="136.66666666666666" y="380.5" width="119.66666666666666" height="48"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="bold" pointSize="40"/>
|
||||
<color key="textColor" red="0.0" green="0.45098039215686275" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" verticalHuggingPriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="Spc-Bt-001">
|
||||
<rect key="frame" x="0.0" y="428.5" width="393" height="200.5"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="商家版" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Lbl-St-001">
|
||||
<rect key="frame" x="161.66666666666666" y="629" width="69.666666666666686" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="bold" pointSize="20"/>
|
||||
<color key="textColor" red="0.0" green="0.45098039215686275" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<edgeInsets key="layoutMargins" top="0.0" left="0.0" bottom="32" right="0.0"/>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Stk-Mn-001" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="Stk-Tp-001"/>
|
||||
<constraint firstItem="Stk-Mn-001" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="Stk-Ld-001"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="Stk-Mn-001" secondAttribute="trailing" id="Stk-Tr-001"/>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="Stk-Mn-001" secondAttribute="bottom" id="Stk-Bt-001"/>
|
||||
<constraint firstItem="Spc-Tp-001" firstAttribute="height" secondItem="Spc-Bt-001" secondAttribute="height" id="Spc-Eq-001"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
@ -22,4 +66,7 @@
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="SplashLogo" width="180" height="180"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@ -116,9 +116,9 @@ struct OrderEntity: Decodable, Equatable {
|
||||
orderTypeLabel = try container.decodeIfPresent(String.self, forKey: .orderTypeLabel) ?? ""
|
||||
depositPayTime = try container.decodeIfPresent(String.self, forKey: .depositPayTime) ?? ""
|
||||
createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
|
||||
photoTravel = try container.decodeIfPresent(PhotoTravelEntity.self, forKey: .photoTravel)
|
||||
photoTravel = try Self.decodePhotoTravel(from: container)
|
||||
refundAmount = try Self.decodeFlexibleString(container: container, key: .refundAmount, defaultValue: "0")
|
||||
multiTravel = try container.decodeIfPresent(MultiTravelEntity.self, forKey: .multiTravel)
|
||||
multiTravel = try Self.decodeMultiTravel(from: container)
|
||||
isNeedEdit = try container.decodeIfPresent(Bool.self, forKey: .isNeedEdit) ?? true
|
||||
isRefined = try container.decodeIfPresent(Int.self, forKey: .isRefined) ?? 0
|
||||
canBindReferral = try container.decodeIfPresent(Int.self, forKey: .canBindReferral) ?? 0
|
||||
@ -145,6 +145,26 @@ struct OrderEntity: Decodable, Equatable {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
private static func decodePhotoTravel(
|
||||
from container: KeyedDecodingContainer<CodingKeys>
|
||||
) throws -> PhotoTravelEntity? {
|
||||
guard container.contains(.photoTravel), try !container.decodeNil(forKey: .photoTravel) else {
|
||||
return nil
|
||||
}
|
||||
let travel = try container.decode(PhotoTravelEntity.self, forKey: .photoTravel)
|
||||
return travel.isEmpty ? nil : travel
|
||||
}
|
||||
|
||||
private static func decodeMultiTravel(
|
||||
from container: KeyedDecodingContainer<CodingKeys>
|
||||
) throws -> MultiTravelEntity? {
|
||||
guard container.contains(.multiTravel), try !container.decodeNil(forKey: .multiTravel) else {
|
||||
return nil
|
||||
}
|
||||
let travel = try container.decode(MultiTravelEntity.self, forKey: .multiTravel)
|
||||
return travel.isEmpty ? nil : travel
|
||||
}
|
||||
}
|
||||
|
||||
struct BoundReferralOrderEntity: Decodable, Equatable {
|
||||
@ -184,6 +204,53 @@ struct BoundReferralOrderEntity: Decodable, Equatable {
|
||||
case status
|
||||
case statusLabel = "status_label"
|
||||
case createdAt = "created_at"
|
||||
case saleUser = "sale_user"
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0
|
||||
orderNo = try container.decodeIfPresent(String.self, forKey: .orderNo) ?? ""
|
||||
saleUserId = try container.decodeIfPresent(Int.self, forKey: .saleUserId) ?? 0
|
||||
salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? ""
|
||||
salerPhone = try container.decodeIfPresent(String.self, forKey: .salerPhone) ?? ""
|
||||
displayText = try container.decodeIfPresent(String.self, forKey: .displayText) ?? ""
|
||||
userMobile = try container.decodeIfPresent(String.self, forKey: .userMobile) ?? ""
|
||||
remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? ""
|
||||
bindOrderNumber = try container.decodeIfPresent(String.self, forKey: .bindOrderNumber) ?? ""
|
||||
sourceType = try container.decodeIfPresent(Int.self, forKey: .sourceType) ?? 0
|
||||
sourceTypeLabel = try container.decodeIfPresent(String.self, forKey: .sourceTypeLabel) ?? ""
|
||||
status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0
|
||||
statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? ""
|
||||
createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
|
||||
|
||||
let saleUser = try container.decodeIfPresent(ReferralSaleUserEntity.self, forKey: .saleUser)
|
||||
photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName)
|
||||
?? saleUser?.photogRemarkName
|
||||
?? saleUser?.displayName
|
||||
?? ""
|
||||
}
|
||||
}
|
||||
|
||||
/// `referral_order.sale_user` 嵌套对象,接口字段与顶层可能重复。
|
||||
struct ReferralSaleUserEntity: Decodable, Equatable {
|
||||
let photogRemarkName: String
|
||||
let displayName: String
|
||||
let salerName: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case photogRemarkName = "photog_remark_name"
|
||||
case displayName = "display_name"
|
||||
case salerName = "saler_name"
|
||||
case name
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? ""
|
||||
let name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
|
||||
salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? name
|
||||
displayName = try container.decodeIfPresent(String.self, forKey: .displayName) ?? salerName
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +289,18 @@ struct PhotoTravelEntity: Decodable, Equatable {
|
||||
retouchGiftPhotoNum = try container.decodeIfPresent(Int.self, forKey: .retouchGiftPhotoNum) ?? 0
|
||||
retouchGiftVideoNum = try container.decodeIfPresent(Int.self, forKey: .retouchGiftVideoNum) ?? 0
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
orderPhotoNum == 0
|
||||
&& orderVideoNum == 0
|
||||
&& !canCancelOrder
|
||||
&& !needCheckIn
|
||||
&& checkInTime.isEmpty
|
||||
&& !canGiftRetouch
|
||||
&& retouchGiftTime.isEmpty
|
||||
&& retouchGiftPhotoNum == 0
|
||||
&& retouchGiftVideoNum == 0
|
||||
}
|
||||
}
|
||||
|
||||
struct MultiTravelEntity: Decodable, Equatable {
|
||||
@ -235,6 +314,10 @@ struct MultiTravelEntity: Decodable, Equatable {
|
||||
self.materialList = materialList
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
materialList.isEmpty
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
materialList = try container.decodeIfPresent([MultiTravelMaterialEntity].self, forKey: .materialList) ?? []
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>随心瞰商家版</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>需要访问相机以扫描订单核销二维码</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
|
||||
@ -82,4 +82,88 @@ final class OrderListViewModelTests: XCTestCase {
|
||||
XCTAssertEqual(order.photoTravel?.orderPhotoNum, 10)
|
||||
XCTAssertEqual(order.photoTravel?.needCheckIn, true)
|
||||
}
|
||||
|
||||
func testOrderListDecodesRealAPIResponseShape() throws {
|
||||
let json = """
|
||||
{
|
||||
"code": 100000,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"total": 3,
|
||||
"list": [
|
||||
{
|
||||
"actual_pay_amount": "0.01",
|
||||
"created_at": "2026-06-25 17:12:24",
|
||||
"is_refined": 1,
|
||||
"multi_travel": {},
|
||||
"order_number": "260625128009",
|
||||
"order_status": 30,
|
||||
"order_status_name": "已完成",
|
||||
"order_type": 14,
|
||||
"order_type_label": "摄影师跟拍线下扫码",
|
||||
"phone": "13222319413",
|
||||
"photo_travel": {},
|
||||
"project_name": "摄影师跟拍线下扫码",
|
||||
"referral_order": {
|
||||
"bind_order_number": "260625128009",
|
||||
"created_at": "2026-06-25 17:13:51",
|
||||
"display_text": "管铜彬1234567 19281871413",
|
||||
"id": 53,
|
||||
"order_no": "LD202606250008",
|
||||
"sale_user": {
|
||||
"display_name": "管铜彬1234567",
|
||||
"name": "管铜彬1234567",
|
||||
"photog_remark_name": "管铜彬1234567",
|
||||
"sale_user_id": 6,
|
||||
"saler_name": "管铜彬1234567"
|
||||
},
|
||||
"sale_user_id": 6,
|
||||
"saler_name": "管铜彬1234567",
|
||||
"saler_phone": "19281871413",
|
||||
"source_type": 2,
|
||||
"source_type_label": "下级线索",
|
||||
"status": 2,
|
||||
"status_label": "已完成",
|
||||
"user_mobile": "13333333331"
|
||||
},
|
||||
"user_id": 141943
|
||||
},
|
||||
{
|
||||
"actual_pay_amount": "0.01",
|
||||
"created_at": "2026-06-17 14:08:37",
|
||||
"is_refined": 2,
|
||||
"multi_travel": {},
|
||||
"order_number": "260617128001",
|
||||
"order_status": 50,
|
||||
"order_status_name": "已退款",
|
||||
"order_type": 11,
|
||||
"phone": "18651857230",
|
||||
"photo_travel": {
|
||||
"can_cancel_order": false,
|
||||
"can_gift_retouch": false,
|
||||
"need_check_in": true,
|
||||
"order_photo_num": 6,
|
||||
"order_video_num": 0,
|
||||
"retouch_gift_photo_num": 2,
|
||||
"retouch_gift_time": "2026-06-23 11:29:33",
|
||||
"retouch_gift_video_num": 0
|
||||
},
|
||||
"project_name": "旅拍 - 便装套餐",
|
||||
"user_id": 143089
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
""".data(using: .utf8)!
|
||||
|
||||
let envelope = try JSONDecoder().decode(APIEnvelope<OrderListResponse<OrderEntity>>.self, from: json)
|
||||
let list = try XCTUnwrap(envelope.data?.list)
|
||||
XCTAssertEqual(list.count, 2)
|
||||
XCTAssertNil(list[0].photoTravel)
|
||||
XCTAssertNil(list[0].multiTravel)
|
||||
XCTAssertEqual(list[0].referralOrder?.orderNo, "LD202606250008")
|
||||
XCTAssertEqual(list[0].referralOrder?.photogRemarkName, "管铜彬1234567")
|
||||
XCTAssertEqual(list[1].photoTravel?.orderPhotoNum, 6)
|
||||
XCTAssertEqual(list[1].photoTravel?.needCheckIn, true)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user